前端页面
This commit is contained in:
4
EdgeAdmin/web/views/@default/httpdns/apps/@menu.html
Normal file
4
EdgeAdmin/web/views/@default/httpdns/apps/@menu.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/apps" code="index">应用列表</menu-item>
|
||||
<a href="" class="item" @click.prevent="createApp()">[添加应用]</a>
|
||||
</first-menu>
|
||||
116
EdgeAdmin/web/views/@default/httpdns/apps/appSettings.html
Normal file
116
EdgeAdmin/web/views/@default/httpdns/apps/appSettings.html
Normal file
@@ -0,0 +1,116 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<style>
|
||||
.httpdns-settings-grid {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.httpdns-settings-grid .left-nav-column {
|
||||
max-width: 220px;
|
||||
}
|
||||
.httpdns-settings-grid .right-form-column {
|
||||
padding-left: .3em !important;
|
||||
}
|
||||
.httpdns-side-menu .item {
|
||||
padding-top: .8em !important;
|
||||
padding-bottom: .8em !important;
|
||||
}
|
||||
.httpdns-mini-action {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
margin-left: .55em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.httpdns-mini-action:hover {
|
||||
color: #1e70bf;
|
||||
}
|
||||
.httpdns-mini-action .icon {
|
||||
margin-right: 0 !important;
|
||||
font-size: .92em !important;
|
||||
}
|
||||
.httpdns-note.comment {
|
||||
color: #8f9aa6 !important;
|
||||
font-size: 12px;
|
||||
margin-top: .45em !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<div class="ui menu text blue">
|
||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{settings.appId}}</code>)</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="appId" :value="app.id" />
|
||||
|
||||
<div class="ui stackable grid httpdns-settings-grid">
|
||||
<div class="three wide computer four wide tablet sixteen wide mobile column left-nav-column">
|
||||
<div class="ui fluid vertical pointing menu httpdns-side-menu">
|
||||
<a href="" class="item" :class="{active: activeSection == 'basic'}" @click.prevent="activeSection='basic'">基础配置</a>
|
||||
<a href="" class="item" :class="{active: activeSection == 'auth'}" @click.prevent="activeSection='auth'">认证与密钥</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="thirteen wide computer twelve wide tablet sixteen wide mobile column right-form-column">
|
||||
<table class="ui table selectable definition" v-show="activeSection == 'basic'">
|
||||
<tr>
|
||||
<td class="title">App ID</td>
|
||||
<td><code>{{settings.appId}}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">应用启用</td>
|
||||
<td><checkbox name="appStatus" value="1" v-model="settings.appStatus"></checkbox></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="title">SNI 防护配置</td>
|
||||
<td>
|
||||
<span class="green">隐匿 SNI</span>
|
||||
<p class="comment httpdns-note">当前默认采用隐匿 SNI 策略,避免在 TLS 握手阶段暴露业务域名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="ui table selectable definition" v-show="activeSection == 'auth'">
|
||||
<tr>
|
||||
<td class="title">请求验签</td>
|
||||
<td>
|
||||
<span class="ui label tiny green" v-if="settings.signEnabled">已开启</span>
|
||||
<span class="ui label tiny basic" v-else>已关闭</span>
|
||||
<a href="" class="ui mini button" :class="settings.signEnabled ? 'basic' : 'primary'" style="margin-left: .8em;" @click.prevent="toggleSignEnabled">{{settings.signEnabled ? "关闭请求验签" : "开启请求验签"}}</a>
|
||||
<p class="comment httpdns-note">打开后,服务端会对请求进行签名校验。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">加签 Secret</td>
|
||||
<td>
|
||||
<code>{{signSecretVisible ? settings.signSecretPlain : settings.signSecretMasked}}</code>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="signSecretVisible = !signSecretVisible" :title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||
<a href="" class="httpdns-mini-action" title="复制加签 Secret" @click.prevent="copySecret(settings.signSecretPlain, '加签 Secret')"><i class="copy outline icon"></i></a>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="resetSignSecret">[重置]</a>
|
||||
<p class="comment httpdns-note">最近更新:{{settings.signSecretUpdatedAt}}</p>
|
||||
<p class="comment httpdns-note" v-if="!settings.signEnabled">请求验签已关闭,当前不使用加签 Secret。</p>
|
||||
<p class="comment httpdns-note">用于生成鉴权接口的安全密钥。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">AES 数据加密 Secret</td>
|
||||
<td>
|
||||
<code>{{aesSecretVisible ? settings.aesSecretPlain : settings.aesSecretMasked}}</code>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="aesSecretVisible = !aesSecretVisible" :title="aesSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="aesSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||
<a href="" class="httpdns-mini-action" title="复制 AES 数据加密 Secret" @click.prevent="copySecret(settings.aesSecretPlain, 'AES Secret')"><i class="copy outline icon"></i></a>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="resetAESSecret">[重置]</a>
|
||||
<p class="comment httpdns-note">最近更新:{{settings.aesSecretUpdatedAt}}</p>
|
||||
<p class="comment httpdns-note">用于解析接口数据加密的密钥。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
111
EdgeAdmin/web/views/@default/httpdns/apps/appSettings.js
Normal file
111
EdgeAdmin/web/views/@default/httpdns/apps/appSettings.js
Normal file
@@ -0,0 +1,111 @@
|
||||
Tea.context(function () {
|
||||
this.activeSection = "basic";
|
||||
this.success = NotifyReloadSuccess("保存成功");
|
||||
this.signSecretVisible = false;
|
||||
this.aesSecretVisible = false;
|
||||
|
||||
this.toggleSignEnabled = function () {
|
||||
let that = this;
|
||||
let targetIsOn = !this.settings.signEnabled;
|
||||
|
||||
if (targetIsOn) {
|
||||
teaweb.confirm("html:开启后,服务端将会对解析请求进行验签鉴权,<span class='red'>未签名、签名无效或过期的请求都解析失败</span>,确认开启吗?", function () {
|
||||
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
||||
.params({
|
||||
appId: that.app.id,
|
||||
isOn: 1
|
||||
})
|
||||
.success(function () {
|
||||
that.settings.signEnabled = true;
|
||||
teaweb.success("请求验签已开启");
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
teaweb.confirm("html:关闭后,服务端将不会对解析请求进行验签鉴权,可能<span class='red'>存在被刷风险</span>,确认关闭吗?", function () {
|
||||
that.$post("/httpdns/apps/app/settings/toggleSignEnabled")
|
||||
.params({
|
||||
appId: that.app.id,
|
||||
isOn: 0
|
||||
})
|
||||
.success(function () {
|
||||
that.settings.signEnabled = false;
|
||||
teaweb.success("请求验签已关闭");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.copySecret = function (text, name) {
|
||||
if (typeof text != "string" || text.length == 0) {
|
||||
teaweb.warn("没有可复制的内容");
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
teaweb.success(name + "已复制");
|
||||
}).catch(function () {
|
||||
this.copyByTextarea(text, name);
|
||||
}.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
this.copyByTextarea(text, name);
|
||||
};
|
||||
|
||||
this.copyByTextarea = function (text, name) {
|
||||
let input = document.createElement("textarea");
|
||||
input.value = text;
|
||||
input.setAttribute("readonly", "readonly");
|
||||
input.style.position = "fixed";
|
||||
input.style.left = "-10000px";
|
||||
input.style.top = "-10000px";
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
|
||||
let ok = false;
|
||||
try {
|
||||
ok = document.execCommand("copy");
|
||||
} catch (e) {
|
||||
ok = false;
|
||||
}
|
||||
document.body.removeChild(input);
|
||||
|
||||
if (ok) {
|
||||
teaweb.success(name + "已复制");
|
||||
} else {
|
||||
teaweb.warn("复制失败,请手动复制");
|
||||
}
|
||||
};
|
||||
|
||||
this.resetSignSecret = function () {
|
||||
let that = this;
|
||||
teaweb.confirm("确定要重置加签 Secret 吗?", function () {
|
||||
that.$post("/httpdns/apps/app/settings/resetSignSecret")
|
||||
.params({
|
||||
appId: that.app.id
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("加签 Secret 已重置", function () {
|
||||
teaweb.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.resetAESSecret = function () {
|
||||
let that = this;
|
||||
teaweb.confirm("确定要重置 AES Secret 吗?", function () {
|
||||
that.$post("/httpdns/apps/app/settings/resetAESSecret")
|
||||
.params({
|
||||
appId: that.app.id
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("AES Secret 已重置", function () {
|
||||
teaweb.reload();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
37
EdgeAdmin/web/views/@default/httpdns/apps/createPopup.html
Normal file
37
EdgeAdmin/web/views/@default/httpdns/apps/createPopup.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>添加应用</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">应用名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="64" ref="focus" />
|
||||
<p class="comment">为该 HTTPDNS 应用设置一个便于识别的名称。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属集群 *</td>
|
||||
<td>
|
||||
<select class="ui dropdown" name="clusterId" v-model="defaultClusterId">
|
||||
<option value="">[请选择集群]</option>
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
<p class="comment">应用的解析请求将由所选集群下的网关节点处理。默认值来自“HTTPDNS 用户设置”。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属用户</td>
|
||||
<td>
|
||||
<select class="ui dropdown" name="userId">
|
||||
<option value="0">[平台自用 / 不指定]</option>
|
||||
<option v-for="user in users" :value="user.id">{{user.name}} ({{user.username}})</option>
|
||||
</select>
|
||||
<p class="comment">可分配给指定租户用户;留空表示平台管理员自用。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
66
EdgeAdmin/web/views/@default/httpdns/apps/customRecords.html
Normal file
66
EdgeAdmin/web/views/@default/httpdns/apps/customRecords.html
Normal file
@@ -0,0 +1,66 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<style>
|
||||
.httpdns-col-ttl {
|
||||
width: 72px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.httpdns-col-actions {
|
||||
width: 130px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<div class="ui menu text blue">
|
||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{app.appId}}</code>)</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<div class="ui small message">
|
||||
当前域名:<strong>{{domain.name}}</strong>
|
||||
</div>
|
||||
|
||||
<a href="" @click.prevent="createRecord" class="ui button primary small" :class="{disabled: !domain || !domain.id}">
|
||||
<i class="icon plus"></i> 新增自定义解析规则
|
||||
</a>
|
||||
<a :href="'/httpdns/apps/domains?appId=' + app.id" class="ui button small">返回域名管理</a>
|
||||
|
||||
<table class="ui table selectable celled" v-if="records.length > 0" style="margin-top: 1em;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>线路</th>
|
||||
<th>规则名称</th>
|
||||
<th>SDNS 参数</th>
|
||||
<th>解析记录</th>
|
||||
<th class="httpdns-col-ttl">TTL</th>
|
||||
<th class="width10">状态</th>
|
||||
<th class="httpdns-col-actions">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="record in records">
|
||||
<td>{{record.lineText}}</td>
|
||||
<td>{{record.ruleName}}</td>
|
||||
<td>{{record.paramsText}}</td>
|
||||
<td>
|
||||
{{record.recordValueText}}
|
||||
</td>
|
||||
<td class="httpdns-col-ttl">{{record.ttl}}s</td>
|
||||
<td>
|
||||
<span class="green" v-if="record.isOn">已启用</span>
|
||||
<span class="grey" v-else>已停用</span>
|
||||
</td>
|
||||
<td class="httpdns-col-actions">
|
||||
<a href="" @click.prevent="editRecord(record.id)">编辑</a> |
|
||||
<a href="" @click.prevent="toggleRecord(record)">{{record.isOn ? "停用" : "启用"}}</a> |
|
||||
<a href="" @click.prevent="deleteRecord(record.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="ui warning message" v-if="!domain || !domain.id">当前应用暂无可用域名,请先到域名管理中添加域名。</p>
|
||||
<p class="ui message" v-else-if="records.length == 0">当前域名还没有自定义解析规则。</p>
|
||||
</div>
|
||||
50
EdgeAdmin/web/views/@default/httpdns/apps/customRecords.js
Normal file
50
EdgeAdmin/web/views/@default/httpdns/apps/customRecords.js
Normal file
@@ -0,0 +1,50 @@
|
||||
Tea.context(function () {
|
||||
if (typeof this.records == "undefined") {
|
||||
this.records = [];
|
||||
}
|
||||
|
||||
this.createRecord = function () {
|
||||
if (!this.domain || !this.domain.id) {
|
||||
return;
|
||||
}
|
||||
teaweb.popup("/httpdns/apps/customRecords/createPopup?appId=" + this.app.id + "&domainId=" + this.domain.id, {
|
||||
width: "56em",
|
||||
height: "40em",
|
||||
title: "新增自定义解析规则"
|
||||
});
|
||||
};
|
||||
|
||||
this.editRecord = function (recordId) {
|
||||
if (!this.domain || !this.domain.id) {
|
||||
return;
|
||||
}
|
||||
teaweb.popup("/httpdns/apps/customRecords/createPopup?appId=" + this.app.id + "&domainId=" + this.domain.id + "&recordId=" + recordId, {
|
||||
width: "56em",
|
||||
height: "40em",
|
||||
title: "编辑自定义解析规则"
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteRecord = function (recordId) {
|
||||
let that = this;
|
||||
teaweb.confirm("确定要删除这条自定义解析规则吗?", function () {
|
||||
that.$post("/httpdns/apps/customRecords/delete")
|
||||
.params({
|
||||
appId: that.app.id,
|
||||
recordId: recordId
|
||||
})
|
||||
.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
this.toggleRecord = function (record) {
|
||||
let that = this;
|
||||
that.$post("/httpdns/apps/customRecords/toggle")
|
||||
.params({
|
||||
appId: that.app.id,
|
||||
recordId: record.id,
|
||||
isOn: record.isOn ? 0 : 1
|
||||
})
|
||||
.refresh();
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<style>
|
||||
.httpdns-inline-actions {
|
||||
margin-top: .6em;
|
||||
}
|
||||
.httpdns-inline-actions .count {
|
||||
color: #8f9aa6;
|
||||
margin-left: .4em;
|
||||
}
|
||||
.httpdns-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .5em;
|
||||
margin-bottom: .45em;
|
||||
}
|
||||
.httpdns-row .field {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.httpdns-row .field.flex {
|
||||
flex: 1;
|
||||
}
|
||||
.httpdns-line-row {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: .5em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3 v-if="isEditing">编辑自定义解析规则</h3>
|
||||
<h3 v-else>新增自定义解析规则</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$">
|
||||
<csrf-token></csrf-token>
|
||||
<input type="hidden" name="appId" :value="app.id" />
|
||||
<input type="hidden" name="domainId" :value="domain.id" />
|
||||
<input type="hidden" name="domain" :value="record.domain" />
|
||||
<input type="hidden" name="recordId" :value="record.id" />
|
||||
<input type="hidden" name="sdnsParamsJSON" :value="JSON.stringify(sdnsParams)" />
|
||||
<input type="hidden" name="recordItemsJSON" :value="JSON.stringify(recordItems)" />
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">域名 *</td>
|
||||
<td><strong>{{record.domain}}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">规则名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="ruleName" maxlength="50" v-model="record.ruleName" ref="focus" />
|
||||
<p class="comment">例如:上海电信灰度-v2。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">线路</td>
|
||||
<td>
|
||||
<div class="httpdns-line-row">
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineScope" v-model="record.lineScope" @change="onLineScopeChange">
|
||||
<option value="china">中国大陆</option>
|
||||
<option value="overseas">港澳台及境外</option>
|
||||
</select>
|
||||
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineCarrier" v-if="record.lineScope == 'china'" v-model="record.lineCarrier">
|
||||
<option v-for="carrier in chinaCarriers" :value="carrier">{{carrier}}</option>
|
||||
</select>
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineRegion" v-if="record.lineScope == 'china'" v-model="record.lineRegion" @change="onChinaRegionChange">
|
||||
<option v-for="region in chinaRegions" :value="region">{{region}}</option>
|
||||
</select>
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineProvince" v-if="record.lineScope == 'china'" v-model="record.lineProvince">
|
||||
<option v-for="province in provinceOptions" :value="province">{{province}}</option>
|
||||
</select>
|
||||
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineContinent" v-if="record.lineScope == 'overseas'" v-model="record.lineContinent" @change="onContinentChange">
|
||||
<option v-for="continent in continents" :value="continent">{{continent}}</option>
|
||||
</select>
|
||||
<select class="ui dropdown auto-width" style="display:inline-block !important;width:auto !important;margin:0 !important;" name="lineCountry" v-if="record.lineScope == 'overseas'" v-model="record.lineCountry">
|
||||
<option v-for="country in countryOptions" :value="country">{{country}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">SDNS 参数配置</td>
|
||||
<td>
|
||||
<div class="httpdns-row" v-for="(param, index) in sdnsParams">
|
||||
<div class="field flex">
|
||||
<input type="text" maxlength="64" placeholder="参数名称" v-model="param.name" />
|
||||
</div>
|
||||
<div class="field flex">
|
||||
<input type="text" maxlength="64" placeholder="参数值" v-model="param.value" />
|
||||
</div>
|
||||
<a href="" @click.prevent="removeSDNSParam(index)" title="删除"><i class="icon trash alternate outline"></i></a>
|
||||
</div>
|
||||
<div class="httpdns-inline-actions">
|
||||
<a href="" @click.prevent="addSDNSParam" :class="{disabled: sdnsParams.length >= 10}">
|
||||
<i class="icon plus circle"></i>添加参数
|
||||
</a>
|
||||
<span class="count">{{sdnsParams.length}}/10</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">解析记录值 *</td>
|
||||
<td>
|
||||
<div style="float:right;">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="weightEnabled" value="1" v-model="record.weightEnabled" />
|
||||
<label>按权重调度</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="httpdns-row" v-for="(item, index) in recordItems">
|
||||
<div class="field">
|
||||
<select class="ui dropdown auto-width" v-model="item.type">
|
||||
<option value="A">A</option>
|
||||
<option value="AAAA">AAAA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field flex">
|
||||
<input type="text" placeholder="记录值(与记录类型匹配)" v-model="item.value" />
|
||||
</div>
|
||||
<div class="field" v-if="record.weightEnabled">
|
||||
<input type="text" style="width:8em;" placeholder="权重(1-100)" v-model="item.weight" />
|
||||
</div>
|
||||
<a href="" @click.prevent="removeRecordItem(index)" title="删除"><i class="icon trash alternate outline"></i></a>
|
||||
</div>
|
||||
<div class="httpdns-inline-actions">
|
||||
<a href="" @click.prevent="addRecordItem" :class="{disabled: recordItems.length >= 10}">
|
||||
<i class="icon plus circle"></i>添加记录值
|
||||
</a>
|
||||
<span class="count">{{recordItems.length}}/10</span>
|
||||
</div>
|
||||
<p class="comment" v-if="record.weightEnabled">开启后每条记录可配置权重,范围 1-100。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">TTL *</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" name="ttl" maxlength="5" style="width:8em;" v-model="record.ttl" />
|
||||
<span class="ui basic label">秒</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">规则状态</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="record.isOn" />
|
||||
<label>启用</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,177 @@
|
||||
Tea.context(function () {
|
||||
var vm = this;
|
||||
|
||||
if (typeof vm.record == "undefined" || vm.record == null) {
|
||||
vm.record = {};
|
||||
}
|
||||
|
||||
vm.chinaCarriers = ["默认", "电信", "联通", "移动", "教育网", "鹏博士", "广电"];
|
||||
vm.chinaRegions = ["默认", "东北", "华北", "华东", "华南", "华中", "西北", "西南"];
|
||||
vm.chinaRegionProvinces = {
|
||||
"默认": ["默认"],
|
||||
"东北": ["默认", "辽宁", "吉林", "黑龙江"],
|
||||
"华北": ["默认", "北京", "天津", "河北", "山西", "内蒙古"],
|
||||
"华东": ["默认", "上海", "江苏", "浙江", "安徽", "福建", "江西", "山东"],
|
||||
"华南": ["默认", "广东", "广西", "海南"],
|
||||
"华中": ["默认", "河南", "湖北", "湖南"],
|
||||
"西北": ["默认", "陕西", "甘肃", "青海", "宁夏", "新疆"],
|
||||
"西南": ["默认", "重庆", "四川", "贵州", "云南", "西藏"]
|
||||
};
|
||||
|
||||
vm.continents = ["默认", "非洲", "南极洲", "亚洲", "欧洲", "北美洲", "南美洲", "大洋洲"];
|
||||
vm.continentCountries = {
|
||||
"默认": ["默认"],
|
||||
"非洲": ["默认", "南非", "埃及", "尼日利亚", "肯尼亚", "摩洛哥"],
|
||||
"南极洲": ["默认"],
|
||||
"亚洲": ["默认", "中国香港", "中国澳门", "中国台湾", "日本", "韩国", "新加坡", "印度", "泰国", "越南"],
|
||||
"欧洲": ["默认", "德国", "英国", "法国", "荷兰", "西班牙", "意大利", "俄罗斯"],
|
||||
"北美洲": ["默认", "美国", "加拿大", "墨西哥"],
|
||||
"南美洲": ["默认", "巴西", "阿根廷", "智利", "哥伦比亚"],
|
||||
"大洋洲": ["默认", "澳大利亚", "新西兰"]
|
||||
};
|
||||
|
||||
vm.parseJSONList = function (raw) {
|
||||
if (typeof raw == "undefined" || raw == null || raw == "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(raw)) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
try {
|
||||
var list = JSON.parse(raw);
|
||||
if (Array.isArray(list)) {
|
||||
return list;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
vm.normalizeBoolean = function (value, defaultValue) {
|
||||
if (typeof value == "boolean") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value == "number") {
|
||||
return value > 0;
|
||||
}
|
||||
if (typeof value == "string") {
|
||||
value = value.toLowerCase();
|
||||
return value == "1" || value == "true" || value == "yes" || value == "on";
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
vm.record.lineScope = vm.record.lineScope == "overseas" ? "overseas" : "china";
|
||||
vm.record.lineCarrier = vm.record.lineCarrier || "默认";
|
||||
vm.record.lineRegion = vm.record.lineRegion || "默认";
|
||||
vm.record.lineProvince = vm.record.lineProvince || "默认";
|
||||
vm.record.lineContinent = vm.record.lineContinent || "默认";
|
||||
vm.record.lineCountry = vm.record.lineCountry || "默认";
|
||||
vm.record.ruleName = vm.record.ruleName || "";
|
||||
vm.record.ttl = vm.record.ttl || 30;
|
||||
vm.record.weightEnabled = vm.normalizeBoolean(vm.record.weightEnabled, false);
|
||||
vm.record.isOn = vm.normalizeBoolean(vm.record.isOn, true);
|
||||
|
||||
vm.sdnsParams = vm.parseJSONList(vm.record.sdnsParamsJson);
|
||||
if (vm.sdnsParams.length == 0) {
|
||||
vm.sdnsParams.push({name: "", value: ""});
|
||||
}
|
||||
|
||||
vm.recordItems = vm.parseJSONList(vm.record.recordItemsJson);
|
||||
if (vm.recordItems.length == 0) {
|
||||
vm.recordItems.push({type: "A", value: "", weight: 100});
|
||||
} else {
|
||||
for (var i = 0; i < vm.recordItems.length; i++) {
|
||||
var item = vm.recordItems[i];
|
||||
if (item.type != "A" && item.type != "AAAA") {
|
||||
item.type = "A";
|
||||
}
|
||||
if (typeof item.weight == "undefined" || item.weight == null || item.weight === "") {
|
||||
item.weight = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vm.provinceOptions = ["默认"];
|
||||
vm.countryOptions = ["默认"];
|
||||
|
||||
vm.refreshProvinceOptions = function () {
|
||||
var provinces = vm.chinaRegionProvinces[vm.record.lineRegion];
|
||||
if (!Array.isArray(provinces) || provinces.length == 0) {
|
||||
provinces = ["默认"];
|
||||
}
|
||||
vm.provinceOptions = provinces;
|
||||
if (vm.provinceOptions.indexOf(vm.record.lineProvince) < 0) {
|
||||
vm.record.lineProvince = vm.provinceOptions[0];
|
||||
}
|
||||
};
|
||||
|
||||
vm.refreshCountryOptions = function () {
|
||||
var countries = vm.continentCountries[vm.record.lineContinent];
|
||||
if (!Array.isArray(countries) || countries.length == 0) {
|
||||
countries = ["默认"];
|
||||
}
|
||||
vm.countryOptions = countries;
|
||||
if (vm.countryOptions.indexOf(vm.record.lineCountry) < 0) {
|
||||
vm.record.lineCountry = vm.countryOptions[0];
|
||||
}
|
||||
};
|
||||
|
||||
vm.onChinaRegionChange = function () {
|
||||
vm.refreshProvinceOptions();
|
||||
};
|
||||
|
||||
vm.onContinentChange = function () {
|
||||
vm.refreshCountryOptions();
|
||||
};
|
||||
|
||||
vm.onLineScopeChange = function () {
|
||||
if (vm.record.lineScope == "overseas") {
|
||||
vm.record.lineContinent = vm.record.lineContinent || "默认";
|
||||
vm.refreshCountryOptions();
|
||||
} else {
|
||||
vm.record.lineCarrier = vm.record.lineCarrier || "默认";
|
||||
vm.record.lineRegion = vm.record.lineRegion || "默认";
|
||||
vm.refreshProvinceOptions();
|
||||
}
|
||||
};
|
||||
|
||||
vm.addSDNSParam = function () {
|
||||
if (vm.sdnsParams.length >= 10) {
|
||||
return;
|
||||
}
|
||||
vm.sdnsParams.push({name: "", value: ""});
|
||||
};
|
||||
|
||||
vm.removeSDNSParam = function (index) {
|
||||
if (index < 0 || index >= vm.sdnsParams.length) {
|
||||
return;
|
||||
}
|
||||
vm.sdnsParams.splice(index, 1);
|
||||
if (vm.sdnsParams.length == 0) {
|
||||
vm.sdnsParams.push({name: "", value: ""});
|
||||
}
|
||||
};
|
||||
|
||||
vm.addRecordItem = function () {
|
||||
if (vm.recordItems.length >= 10) {
|
||||
return;
|
||||
}
|
||||
vm.recordItems.push({type: "A", value: "", weight: 100});
|
||||
};
|
||||
|
||||
vm.removeRecordItem = function (index) {
|
||||
if (index < 0 || index >= vm.recordItems.length) {
|
||||
return;
|
||||
}
|
||||
vm.recordItems.splice(index, 1);
|
||||
if (vm.recordItems.length == 0) {
|
||||
vm.recordItems.push({type: "A", value: "", weight: 100});
|
||||
}
|
||||
};
|
||||
|
||||
vm.onLineScopeChange();
|
||||
});
|
||||
35
EdgeAdmin/web/views/@default/httpdns/apps/domains.html
Normal file
35
EdgeAdmin/web/views/@default/httpdns/apps/domains.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div>
|
||||
<div class="ui menu text blue">
|
||||
<div class="item"><strong>{{app.name}}</strong> (<code>{{app.appId}}</code>)</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<a href="" @click.prevent="bindDomain" class="ui button primary small"><i class="icon plus"></i>添加域名</a>
|
||||
|
||||
<table class="ui table selectable celled" v-if="domains.length > 0" style="margin-top: 1em;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>域名列表</th>
|
||||
<th class="two wide">规则策略</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="domain in domains">
|
||||
<td><strong>{{domain.name}}</strong></td>
|
||||
<td>
|
||||
<a :href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">{{domain.customRecordCount}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'/httpdns/apps/customRecords?appId=' + app.id + '&domainId=' + domain.id">自定义解析</a> |
|
||||
<a href="" @click.prevent="deleteDomain(domain.id)">解绑</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="ui message" v-if="domains.length == 0">该应用尚未绑定域名。</p>
|
||||
</div>
|
||||
24
EdgeAdmin/web/views/@default/httpdns/apps/domains.js
Normal file
24
EdgeAdmin/web/views/@default/httpdns/apps/domains.js
Normal file
@@ -0,0 +1,24 @@
|
||||
Tea.context(function () {
|
||||
if (typeof this.domains == "undefined") {
|
||||
this.domains = [];
|
||||
}
|
||||
|
||||
this.bindDomain = function () {
|
||||
teaweb.popup("/httpdns/apps/domains/createPopup?appId=" + this.app.id, {
|
||||
height: "24em",
|
||||
width: "46em",
|
||||
title: "添加域名"
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteDomain = function (domainId) {
|
||||
let that = this;
|
||||
teaweb.confirm("确定要解绑这个域名吗?", function () {
|
||||
that.$post("/httpdns/apps/domains/delete")
|
||||
.params({
|
||||
domainId: domainId
|
||||
})
|
||||
.refresh();
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>添加域名</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">域名 *</td>
|
||||
<td>
|
||||
<input type="text" name="domain" maxlength="255" placeholder="api.example.com" ref="focus" />
|
||||
<p class="comment">请输入完整 FQDN,例如 <code>api.example.com</code>。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
53
EdgeAdmin/web/views/@default/httpdns/apps/index.html
Normal file
53
EdgeAdmin/web/views/@default/httpdns/apps/index.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div>
|
||||
<form method="get" class="ui form" action="/httpdns/apps">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" style="width:16em" placeholder="AppID 或应用名称..." :value="keyword" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
<a href="/httpdns/apps" v-if="keyword.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="ui message" v-if="apps.length == 0">暂时没有符合条件的 HTTPDNS 应用。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="apps.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>应用名称</th>
|
||||
<th>AppID</th>
|
||||
<th>绑定域名数</th>
|
||||
<th class="one wide center">状态</th>
|
||||
<th class="tz op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="app in apps">
|
||||
<td>
|
||||
<a :href="'/httpdns/apps/app?appId=' + app.id">
|
||||
<strong><keyword :v-word="keyword">{{app.name}}</keyword></strong>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{app.appId}}</code>
|
||||
<copy-icon :text="app.appId"></copy-icon>
|
||||
</td>
|
||||
<td><a :href="'/httpdns/apps/domains?appId=' + app.id">{{app.domainCount}}</a></td>
|
||||
<td class="center">
|
||||
<label-on :v-is-on="app.isOn"></label-on>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'/httpdns/apps/app?appId=' + app.id">域名管理</a> |
|
||||
<a :href="'/httpdns/apps/app/settings?appId=' + app.id">应用设置</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
</div>
|
||||
13
EdgeAdmin/web/views/@default/httpdns/apps/index.js
Normal file
13
EdgeAdmin/web/views/@default/httpdns/apps/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
Tea.context(function () {
|
||||
if (typeof this.apps == "undefined") {
|
||||
this.apps = [];
|
||||
}
|
||||
|
||||
this.createApp = function () {
|
||||
teaweb.popup("/httpdns/apps/createPopup", {
|
||||
height: "26em",
|
||||
width: "48em",
|
||||
title: "添加应用"
|
||||
});
|
||||
};
|
||||
});
|
||||
78
EdgeAdmin/web/views/@default/httpdns/apps/policies.html
Normal file
78
EdgeAdmin/web/views/@default/httpdns/apps/policies.html
Normal file
@@ -0,0 +1,78 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<h3 class="ui header">HTTPDNS 鍏ㄥ眬绛栫暐</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">鍏ㄥ眬榛樿瑙f瀽 TTL</td>
|
||||
<td><input type="text" name="defaultTTL" maxlength="8" v-model="policies.defaultTTL" /> 绉?/td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">鍏ㄥ眬榛樿 SNI 绛夌骇</td>
|
||||
<td>
|
||||
<select name="defaultSniPolicy" class="ui dropdown auto-width" v-model="policies.defaultSniPolicy">
|
||||
<option value="level1">level1锛堝浐瀹?SNI锛?/option>
|
||||
<option value="level2">level2锛堥殣鍖?SNI锛?/option>
|
||||
<option value="level3">level3锛圗CH锛?/option>
|
||||
</select>
|
||||
<div class="grey small" v-if="policies.defaultSniPolicy == 'level1'" style="margin-top:.5em;">
|
||||
level1 浠呬娇鐢ㄥ浐瀹?SNI锛屼笉鍚敤 ECS 鍜岃瘉涔︽牎楠岀瓥鐣ャ€? </div>
|
||||
<div class="grey small" v-else-if="policies.defaultSniPolicy == 'level2'" style="margin-top:.5em;">
|
||||
level2 浠呭惎鐢ㄩ殣鍖?SNI锛屼笉瑕佹眰閰嶇疆 ECS 涓庤瘉涔︾瓥鐣ャ€? </div>
|
||||
<div class="grey small" v-else style="margin-top:.5em;">
|
||||
level3 鍚敤 ECH锛屽缓璁悓鏃堕厤缃?ECS 涓庤瘉涔︽牎楠岀瓥鐣ャ€? </div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">鍏ㄥ眬闄嶇骇瓒呮椂</td>
|
||||
<td><input type="text" name="defaultFallbackMs" maxlength="8" v-model="policies.defaultFallbackMs" /> 姣</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="policies.defaultSniPolicy == 'level3'">
|
||||
<td class="title">全局 ECS 掩码策略</td>
|
||||
<td>
|
||||
<select name="ecsMode" class="ui dropdown auto-width" v-model="policies.ecsMode">
|
||||
<option value="off">鍏抽棴</option>
|
||||
<option value="auto">鑷姩</option>
|
||||
<option value="custom">鑷畾涔?/option>
|
||||
</select>
|
||||
<span v-if="policies.ecsMode == 'custom'">
|
||||
<span class="grey" style="margin-left:.8em;">IPv4 /</span>
|
||||
<input type="text" name="ecsIPv4Prefix" maxlength="3" v-model="policies.ecsIPv4Prefix" style="width:4.5em;" />
|
||||
<span class="grey" style="margin-left:.8em;">IPv6 /</span>
|
||||
<input type="text" name="ecsIPv6Prefix" maxlength="3" v-model="policies.ecsIPv6Prefix" style="width:4.5em;" />
|
||||
</span>
|
||||
<span v-else class="grey" style="margin-left:.8em;">仅在“自定义”模式下配置掩码。</span>
|
||||
|
||||
<input type="hidden" name="ecsIPv4Prefix" v-model="policies.ecsIPv4Prefix" v-if="policies.ecsMode != 'custom'" />
|
||||
<input type="hidden" name="ecsIPv6Prefix" v-model="policies.ecsIPv6Prefix" v-if="policies.ecsMode != 'custom'" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-show="policies.defaultSniPolicy == 'level3'">
|
||||
<td class="title">鍏ㄥ眬璇佷功鏍¢獙绛栫暐</td>
|
||||
<td>
|
||||
<span class="grey">证书指纹校验(Pinning)</span>
|
||||
<select name="pinningMode" class="ui dropdown auto-width" v-model="policies.pinningMode" style="margin-left:.4em;">
|
||||
<option value="off">鍏抽棴</option>
|
||||
<option value="report">瑙傚療妯″紡</option>
|
||||
<option value="enforce">寮哄埗鏍¢獙</option>
|
||||
</select>
|
||||
<span class="grey" style="margin-left:1.2em;">证书 SAN 域名校验</span>
|
||||
<select name="sanMode" class="ui dropdown auto-width" v-model="policies.sanMode" style="margin-left:.4em;">
|
||||
<option value="off">鍏抽棴</option>
|
||||
<option value="report">瑙傚療妯″紡</option>
|
||||
<option value="strict">涓ユ牸鏍¢獙</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
|
||||
40
EdgeAdmin/web/views/@default/httpdns/apps/policies.js
Normal file
40
EdgeAdmin/web/views/@default/httpdns/apps/policies.js
Normal file
@@ -0,0 +1,40 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyReloadSuccess("保存成功");
|
||||
|
||||
this.$delay(function () {
|
||||
this.$watch("policies.defaultSniPolicy", function (level) {
|
||||
if (level == "level1" || level == "level2") {
|
||||
this.policies.ecsMode = "off";
|
||||
this.policies.pinningMode = "off";
|
||||
this.policies.sanMode = "off";
|
||||
return;
|
||||
}
|
||||
|
||||
if (level == "level3") {
|
||||
if (this.policies.ecsMode == "off") {
|
||||
this.policies.ecsMode = "auto";
|
||||
}
|
||||
if (this.policies.pinningMode == "off") {
|
||||
this.policies.pinningMode = "report";
|
||||
}
|
||||
if (this.policies.sanMode == "off") {
|
||||
this.policies.sanMode = "strict";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$watch("policies.ecsMode", function (mode) {
|
||||
if (this.policies.defaultSniPolicy != "level3") {
|
||||
return;
|
||||
}
|
||||
if (mode == "custom") {
|
||||
if (!this.policies.ecsIPv4Prefix || this.policies.ecsIPv4Prefix <= 0) {
|
||||
this.policies.ecsIPv4Prefix = 24;
|
||||
}
|
||||
if (!this.policies.ecsIPv6Prefix || this.policies.ecsIPv6Prefix <= 0) {
|
||||
this.policies.ecsIPv6Prefix = 56;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
<second-menu>
|
||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">{{cluster.name}}</menu-item>
|
||||
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id" code="index">节点列表</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/createNode?clusterId=' + cluster.id" code="createNode">创建节点</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/cluster/settings?clusterId=' + cluster.id" code="settings">集群设置</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/delete?clusterId=' + cluster.id" code="delete">删除集群</menu-item>
|
||||
</second-menu>
|
||||
6
EdgeAdmin/web/views/@default/httpdns/clusters/@menu.html
Normal file
6
EdgeAdmin/web/views/@default/httpdns/clusters/@menu.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/clusters" code="index">集群列表</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<menu-item href="/httpdns/clusters/create" code="create">[创建新集群]</menu-item>
|
||||
</first-menu>
|
||||
30
EdgeAdmin/web/views/@default/httpdns/clusters/certs.html
Normal file
30
EdgeAdmin/web/views/@default/httpdns/clusters/certs.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
<h3 class="ui header">HTTPS 接口证书</h3>
|
||||
<p class="comment">用于 HTTPDNS 接口证书管理与展示。</p>
|
||||
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:45%;">证书名称</th>
|
||||
<th style="width:25%;">颁发机构</th>
|
||||
<th style="width:20%;">到期时间</th>
|
||||
<th style="width:10%;">证书ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="cert in certs">
|
||||
<td>{{cert.name}}</td>
|
||||
<td>{{cert.issuer}}</td>
|
||||
<td>{{cert.expiresAt}}</td>
|
||||
<td><code>{{cert.id}}</code></td>
|
||||
</tr>
|
||||
<tr v-if="!certs || certs.length == 0">
|
||||
<td colspan="4" class="grey">暂无证书。</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
91
EdgeAdmin/web/views/@default/httpdns/clusters/cluster.html
Normal file
91
EdgeAdmin/web/views/@default/httpdns/clusters/cluster.html
Normal file
@@ -0,0 +1,91 @@
|
||||
{$layout}
|
||||
{$template "cluster_menu"}
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<div class="right-menu">
|
||||
<a :href="'/httpdns/clusters/createNode?clusterId=' + clusterId" class="item">[创建节点]</a>
|
||||
</div>
|
||||
|
||||
<form class="ui form" action="/httpdns/clusters/cluster">
|
||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="installedState" v-model="installState">
|
||||
<option value="0">[安装状态]</option>
|
||||
<option value="1">已安装</option>
|
||||
<option value="2">未安装</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="activeState" v-model="activeState">
|
||||
<option value="0">[在线状态]</option>
|
||||
<option value="1">在线</option>
|
||||
<option value="2">离线</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" placeholder="节点名称、IP..." :value="keyword" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button class="ui button" type="submit">搜索</button>
|
||||
|
||||
<a :href="'/httpdns/clusters/cluster?clusterId=' + clusterId"
|
||||
v-if="installState > 0 || activeState > 0 || keyword.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="ui margin" v-if="!hasNodes"></div>
|
||||
<p class="comment" v-if="!hasNodes">当前集群下暂时还没有节点。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="hasNodes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>节点名称</th>
|
||||
<th>IP地址</th>
|
||||
<th class="width-5 center">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-for="node in nodes">
|
||||
<tr>
|
||||
<td>
|
||||
<a
|
||||
:href="'/httpdns/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">{{node.name}}</a>
|
||||
<a :href="'/httpdns/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id"
|
||||
title="设置"><i class="icon setting grey"></i></a>
|
||||
<div v-if="node.region != null">
|
||||
<span class="grey small">{{node.region.name}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="node.ipAddresses.length == 0" class="disabled">-</span>
|
||||
<div v-for="addr in node.ipAddresses">
|
||||
<div class="ui label tiny basic">{{addr.ip}}
|
||||
<span class="small" v-if="addr.name.length > 0">({{addr.name}})</span>
|
||||
<span class="small red" v-if="!addr.canAccess" title="不公开访问">[内]</span>
|
||||
<span class="small red" v-if="!addr.isOn">[下线]</span>
|
||||
<span class="small red" v-if="!addr.isUp" title="健康检查失败">[宕机]</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="center">
|
||||
<div v-if="!node.isUp">
|
||||
<span class="red">宕机</span>
|
||||
</div>
|
||||
<div v-else-if="!node.isOn">
|
||||
<span class="disabled">未启用</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-if="node.status.isActive" class="green">在线</span>
|
||||
<span v-else class="disabled">离线</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'/httpdns/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id">详情</a>
|
||||
<a href="" @click.prevent="deleteNode(node.id)">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="page" v-html="page"></div>
|
||||
17
EdgeAdmin/web/views/@default/httpdns/clusters/cluster.js
Normal file
17
EdgeAdmin/web/views/@default/httpdns/clusters/cluster.js
Normal file
@@ -0,0 +1,17 @@
|
||||
Tea.context(function () {
|
||||
this.deleteNode = function (nodeId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除此节点吗?", function () {
|
||||
that.$post("/httpdns/clusters/deleteNode")
|
||||
.params({
|
||||
clusterId: that.clusterId,
|
||||
nodeId: nodeId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
<second-menu>
|
||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + currentCluster.id">{{currentCluster.name}}</menu-item>
|
||||
<span class="item disabled" style="padding-left: 0; padding-right: 0">»</span>
|
||||
<menu-item :href="'/httpdns/clusters/cluster?clusterId=' + clusterId">节点列表</menu-item>
|
||||
<span class="item disabled">|</span>
|
||||
<menu-item :href="'/httpdns/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
|
||||
<menu-item :href="'/httpdns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
|
||||
</second-menu>
|
||||
@@ -0,0 +1,4 @@
|
||||
a.underline {
|
||||
border-bottom: 1px #db2828 dashed;
|
||||
}
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"}
|
||||
@@ -0,0 +1,191 @@
|
||||
{$layout}
|
||||
|
||||
{$template "node_menu"}
|
||||
|
||||
<h3>节点详情</h3>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点名称</td>
|
||||
<td>{{node.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>状态</td>
|
||||
<td><label-on :v-is-on="node.isOn"></label-on></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP地址</td>
|
||||
<td>
|
||||
<div v-if="node.ipAddresses.length > 0">
|
||||
<div>
|
||||
<div v-for="(address, index) in node.ipAddresses" class="ui label tiny basic">
|
||||
{{address.ip}}
|
||||
<span class="small" v-if="address.name.length > 0">({{address.name}}<span v-if="!address.canAccess">,不公开访问</span>)</span>
|
||||
<span class="small red" v-if="!address.isOn">[off]</span>
|
||||
<span class="small red" v-if="!address.isUp">[down]</span>
|
||||
<span class="small" v-if="address.name.length == 0 && !address.canAccess">(不公开访问)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="disabled">暂时还没有填写 IP 地址。</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>SSH主机地址</td>
|
||||
<td>
|
||||
<div v-if="node.login != null && node.login.params != null && node.login.params.host != null">
|
||||
<span v-if="node.login.params.host.length > 0">{{node.login.params.host}}</span>
|
||||
<span v-else class="disabled">尚未设置</span>
|
||||
</div>
|
||||
<div v-else class="disabled">尚未设置</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH主机端口</td>
|
||||
<td>
|
||||
<div v-if="node.login != null && node.login.params != null && node.login.params.host != null">
|
||||
<span v-if="node.login.params.port > 0">{{node.login.params.port}}</span>
|
||||
<span v-else class="disabled">尚未设置</span>
|
||||
</div>
|
||||
<span v-else class="disabled">尚未设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH登录认证</td>
|
||||
<td>
|
||||
<div v-if="node.login != null && node.login.grant != null && node.login.grant.id > 0">
|
||||
<a :href="'/clusters/grants/grant?grantId=' + node.login.grant.id">
|
||||
{{node.login.grant.name}}
|
||||
<span class="small grey">({{node.login.grant.methodName}})</span>
|
||||
<span class="small grey" v-if="node.login.grant.username.length > 0">({{node.login.grant.username}})</span>
|
||||
</a>
|
||||
</div>
|
||||
<span v-else class="disabled">尚未设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>API节点地址</td>
|
||||
<td>
|
||||
<div v-if="node.apiNodeAddrs != null && node.apiNodeAddrs.length > 0">
|
||||
<span v-for="addr in node.apiNodeAddrs" class="ui label basic small">{{addr}}</span>
|
||||
</div>
|
||||
<span v-else class="disabled">使用全局设置</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<h3>运行状态</h3>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">运行状态</td>
|
||||
<td>
|
||||
<div v-if="node.status.isActive">
|
||||
<span class="green">运行中</span>
|
||||
<a href="" @click.prevent="stopNode()" v-if="!isStopping"><span>[通过SSH停止]</span></a>
|
||||
<span v-if="isStopping">[停止中...]</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="red">已断开</span>
|
||||
<a href="" @click.prevent="startNode()" v-if="node.isInstalled && !isStarting"><span>[通过SSH启动]</span></a>
|
||||
<span v-if="node.isInstalled && isStarting">[启动中...]</span>
|
||||
<a v-if="!node.isInstalled" :href="'/httpdns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id"><span>去安装 ></span></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody v-show="node.status.isActive">
|
||||
<tr>
|
||||
<td>CPU用量</td>
|
||||
<td>
|
||||
{{node.status.cpuUsageText}}
|
||||
|
||||
<span v-if="node.status.cpuPhysicalCount > 0" class="small grey">({{node.status.cpuPhysicalCount}}核心/{{node.status.cpuLogicalCount}}线程)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>内存用量</td>
|
||||
<td>{{node.status.memUsageText}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>负载</td>
|
||||
<td>
|
||||
{{node.status.load1m}} {{node.status.load5m}} {{node.status.load15m}}
|
||||
|
||||
<tip-icon content="三个数字分别代表1分钟、5分钟、15分钟平均负载"></tip-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr v-if="node.status.buildVersion != null && node.status.buildVersion.length > 0">
|
||||
<td>版本</td>
|
||||
<td>
|
||||
v{{node.status.buildVersion}}
|
||||
|
||||
<a :href="'/httpdns/clusters/upgradeRemote?clusterId=' + clusterId + '&nodeId=' + node.id" v-if="shouldUpgrade"><span class="red">发现新版本 v{{newVersion}} »</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="node.status.exePath != null && node.status.exePath.length > 0">
|
||||
<td>主程序位置</td>
|
||||
<td>{{node.status.exePath}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>最近API连接状况</td>
|
||||
<td>
|
||||
<span v-if="node.status.apiSuccessPercent > 0 && node.status.apiAvgCostSeconds > 0">
|
||||
<span v-if="node.status.apiSuccessPercent <= 50" class="red">连接错误异常严重({{round(100 - node.status.apiSuccessPercent)}}%失败),请改善当前节点和API节点之间通讯</span>
|
||||
<span v-else-if="node.status.apiSuccessPercent <= 80" class="red">连接错误较多({{round(100 - node.status.apiSuccessPercent)}}%失败),请改善当前节点和API节点之间通讯</span>
|
||||
<span v-else-if="node.status.apiSuccessPercent < 100" class="orange">有连接错误发生({{round(100 - node.status.apiSuccessPercent)}}%失败),请改善当前节点和API节点之间通讯</span>
|
||||
<span v-else>
|
||||
<span v-if="node.status.apiAvgCostSeconds <= 1" class="green">连接良好</span>
|
||||
<span v-else-if="node.status.apiAvgCostSeconds <= 5" class="orange">连接基本稳定(平均{{round(node.status.apiAvgCostSeconds)}}秒)</span>
|
||||
<span v-else-if="node.status.apiAvgCostSeconds <= 10" class="orange">连接速度较慢(平均{{round(node.status.apiAvgCostSeconds)}}秒)</span>
|
||||
<span v-else class="red">连接非常慢(平均{{round(node.status.apiAvgCostSeconds)}}秒),请改善当前节点和API节点之间通讯</span>
|
||||
</span>
|
||||
</span>
|
||||
<span v-else class="disabled">尚未上报数据</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="nodeDatetime.length > 0">
|
||||
<td>上次更新时间</td>
|
||||
<td>
|
||||
{{nodeDatetime}}
|
||||
<p class="comment" v-if="nodeTimeDiff > 30"><span class="red">当前节点时间与API节点时间相差 {{nodeTimeDiff}} 秒,请同步节点时间。</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="comment" v-if="node.status.isActive">每隔30秒钟更新一次运行状态。</p>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
<h3>安装信息</h3>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td>节点ID <em>(id)</em></td>
|
||||
<td>{{node.uniqueId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>密钥 <em>(secret)</em></td>
|
||||
<td>{{node.secret}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">安装目录</td>
|
||||
<td>
|
||||
<div v-if="node.installDir.length == 0">使用集群设置<span v-if="node.cluster != null && node.cluster.installDir.length > 0">({{node.cluster.installDir}})</span></div>
|
||||
<span v-else>{{node.installDir}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>已安装</td>
|
||||
<td>
|
||||
<span v-if="node.isInstalled" class="green">已安装</span>
|
||||
<a v-else :href="'/httpdns/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" class="underline" title="点击进入安装界面"><span class="red">未安装</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
Tea.context(function () {
|
||||
this.isStarting = false
|
||||
this.startNode = function () {
|
||||
this.isStarting = true
|
||||
this.$post('/httpdns/clusters/cluster/node/start')
|
||||
.params({
|
||||
nodeId: this.node.id
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success('启动成功', function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
.done(function () {
|
||||
this.isStarting = false
|
||||
})
|
||||
}
|
||||
|
||||
this.isStopping = false
|
||||
this.stopNode = function () {
|
||||
this.isStopping = true
|
||||
this.$post('/httpdns/clusters/cluster/node/stop')
|
||||
.params({
|
||||
nodeId: this.node.id
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success('执行成功', function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
})
|
||||
.done(function () {
|
||||
this.isStopping = false
|
||||
})
|
||||
}
|
||||
|
||||
this.round = function (f) {
|
||||
return Math.round(f * 100) / 100
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
a.underline {
|
||||
border-bottom: 1px #db2828 dashed;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.installing-box {
|
||||
line-height: 1.8;
|
||||
}
|
||||
.installing-box .blue {
|
||||
color: #2185d0;
|
||||
}
|
||||
/*# sourceMappingURL=install.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["install.less"],"names":[],"mappings":"AAAA;EACC,gBAAA;;AADD,eAGC;EACC,cAAA","file":"install.css"}
|
||||
@@ -0,0 +1,100 @@
|
||||
{$layout}
|
||||
{$template "node_menu"}
|
||||
{$template "/code_editor"}
|
||||
|
||||
<!-- 已安装 -->
|
||||
<div v-if="node.isInstalled">
|
||||
<div class="ui message green">当前节点为已安装状态。</div>
|
||||
<a href="" @click.prevent="updateNodeIsInstalled(false)">[重新安装]</a>
|
||||
|
||||
<h4>配置文件</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">配置文件</td>
|
||||
<td>
|
||||
configs/api_httpdns.yaml
|
||||
<download-link :v-element="'rpc-code'" :v-file="'api_httpdns.yaml'">[下载]</download-link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>配置内容</td>
|
||||
<td>
|
||||
<source-code-box id="rpc-code" type="text/yaml">rpc.endpoints: [ {{apiEndpoints}} ]
|
||||
nodeId: "{{node.uniqueId}}"
|
||||
secret: "{{node.secret}}"</source-code-box>
|
||||
<p class="comment">每个节点的配置文件内容均不相同,不能混用。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">安装目录</td>
|
||||
<td>
|
||||
<div v-if="node.installDir.length == 0">使用集群设置<span
|
||||
v-if="node.cluster != null && node.cluster.installDir.length > 0">({{node.cluster.installDir}})</span>
|
||||
</div>
|
||||
<span v-else>{{node.installDir}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 未安装 -->
|
||||
<div v-if="!node.isInstalled">
|
||||
<h4>方法1:通过SSH自动安装</h4>
|
||||
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">SSH地址</td>
|
||||
<td>
|
||||
<span v-if="sshAddr.length > 0">{{sshAddr}} <a href=""
|
||||
@click.prevent="showSSHPopup(nodeId)">[修改]</a></span>
|
||||
<span v-else><span class="red">尚未设置</span> <a href=""
|
||||
@click.prevent="showSSHPopup(nodeId)">[设置]</a></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div v-if="installStatus != null && (installStatus.isRunning || installStatus.isFinished)"
|
||||
class="ui segment installing-box">
|
||||
<div v-if="installStatus.isRunning" class="blue">安装中...</div>
|
||||
<div v-if="installStatus.isFinished">
|
||||
<span v-if="installStatus.isOk" class="green">已安装成功</span>
|
||||
<span v-if="!installStatus.isOk" class="red">安装过程中发生错误:{{installStatus.error}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="installStatus != null && installStatus.isFinished">
|
||||
<button class="ui button small" type="button" @click.prevent="install()">重新安装</button>
|
||||
</div>
|
||||
<div v-if="installStatus == null || (!installStatus.isFinished && !installStatus.isRunning)">
|
||||
<button class="ui button small primary" type="button" @click.prevent="install()">开始安装</button>
|
||||
</div>
|
||||
|
||||
<h4>方法2:手动安装</h4>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">配置文件</td>
|
||||
<td>
|
||||
configs/api_httpdns.yaml
|
||||
<download-link :v-element="'rpc-code'" :v-file="'api_httpdns.yaml'">[下载]</download-link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>配置内容</td>
|
||||
<td>
|
||||
<source-code-box id="rpc-code" type="text/yaml">rpc.endpoints: [ {{apiEndpoints}} ]
|
||||
nodeId: "{{node.uniqueId}}"
|
||||
secret: "{{node.secret}}"</source-code-box>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">安装目录</td>
|
||||
<td>
|
||||
<div v-if="node.installDir.length == 0">使用集群设置<span
|
||||
v-if="node.cluster != null && node.cluster.installDir.length > 0">({{node.cluster.installDir}})</span>
|
||||
</div>
|
||||
<span v-else>{{node.installDir}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="" @click.prevent="updateNodeIsInstalled(true)">[修改为已安装状态]</a>
|
||||
</div>
|
||||
@@ -0,0 +1,115 @@
|
||||
Tea.context(function () {
|
||||
let isInstalling = false
|
||||
|
||||
this.$delay(function () {
|
||||
this.reloadStatus(this.nodeId)
|
||||
})
|
||||
|
||||
this.install = function () {
|
||||
isInstalling = true
|
||||
|
||||
this.$post("$")
|
||||
.params({
|
||||
nodeId: this.nodeId
|
||||
})
|
||||
.success(function () {})
|
||||
}
|
||||
|
||||
this.updateNodeIsInstalled = function (isInstalled) {
|
||||
let msg = isInstalled
|
||||
? "html:确认要将当前节点修改为 <strong>已安装</strong> 状态?"
|
||||
: "html:确认要将当前节点修改为 <strong>未安装</strong> 状态?"
|
||||
teaweb.confirm(msg, function () {
|
||||
this.$post("/httpdns/clusters/cluster/node/updateInstallStatus")
|
||||
.params({
|
||||
nodeId: this.nodeId,
|
||||
isInstalled: isInstalled ? 1 : 0
|
||||
})
|
||||
.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
this.reloadStatus = function (nodeId) {
|
||||
let that = this
|
||||
|
||||
this.$post("/httpdns/clusters/cluster/node/status")
|
||||
.params({
|
||||
nodeId: nodeId
|
||||
})
|
||||
.success(function (resp) {
|
||||
this.installStatus = resp.data.installStatus
|
||||
this.node.isInstalled = resp.data.isInstalled
|
||||
|
||||
if (!isInstalling) {
|
||||
return
|
||||
}
|
||||
|
||||
let currentNodeId = this.node.id
|
||||
let installStatus = this.installStatus || {}
|
||||
let errMsg = installStatus.error || ""
|
||||
let errorCode = installStatus.errorCode || ""
|
||||
|
||||
if (errorCode.length > 0) {
|
||||
isInstalling = false
|
||||
}
|
||||
|
||||
switch (errorCode) {
|
||||
case "EMPTY_LOGIN":
|
||||
case "EMPTY_SSH_HOST":
|
||||
case "EMPTY_SSH_PORT":
|
||||
case "EMPTY_GRANT":
|
||||
teaweb.warn("需要补充 SSH 登录信息", function () {
|
||||
teaweb.popup("/httpdns/clusters/updateNodeSSH?nodeId=" + currentNodeId, {
|
||||
height: "30em",
|
||||
callback: function () {
|
||||
that.install()
|
||||
}
|
||||
})
|
||||
})
|
||||
return
|
||||
case "SSH_LOGIN_FAILED":
|
||||
teaweb.warn("SSH 登录失败,请检查设置", function () {
|
||||
teaweb.popup("/httpdns/clusters/updateNodeSSH?nodeId=" + currentNodeId, {
|
||||
height: "30em",
|
||||
callback: function () {
|
||||
that.install()
|
||||
}
|
||||
})
|
||||
})
|
||||
return
|
||||
case "CREATE_ROOT_DIRECTORY_FAILED":
|
||||
teaweb.warn("创建根目录失败:" + errMsg)
|
||||
return
|
||||
case "INSTALL_HELPER_FAILED":
|
||||
teaweb.warn("安装助手失败:" + errMsg)
|
||||
return
|
||||
case "TEST_FAILED":
|
||||
teaweb.warn("环境测试失败:" + errMsg)
|
||||
return
|
||||
case "RPC_TEST_FAILED":
|
||||
teaweb.confirm("html:节点到 API 的 RPC 连通性测试失败:" + errMsg + "<br/>现在去修改 API 信息?", function () {
|
||||
window.location = "/settings/api"
|
||||
})
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
.done(function () {
|
||||
this.$delay(function () {
|
||||
this.reloadStatus(nodeId)
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
this.showSSHPopup = function (nodeId) {
|
||||
teaweb.popup("/httpdns/clusters/updateNodeSSH?nodeId=" + nodeId, {
|
||||
height: "30em",
|
||||
callback: function () {
|
||||
teaweb.success("保存成功", function () {
|
||||
teaweb.reload()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
.installing-box {
|
||||
line-height: 1.8;
|
||||
|
||||
.blue {
|
||||
color: #2185d0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pre.log-box {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/*# sourceMappingURL=logs.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["logs.less"],"names":[],"mappings":"AAAA,GAAG;EACF,SAAA;EACA,UAAA","file":"logs.css"}
|
||||
@@ -0,0 +1,51 @@
|
||||
{$layout}
|
||||
{$template "node_menu"}
|
||||
{$template "/datepicker"}
|
||||
|
||||
<form method="get" action="/httpdns/clusters/cluster/node/logs" class="ui form" autocomplete="off">
|
||||
<input type="hidden" name="clusterId" :value="clusterId"/>
|
||||
<input type="hidden" name="nodeId" :value="nodeId"/>
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" value="" style="width:8em" id="day-from-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" value="" style="width:8em" id="day-to-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="level" v-model="level">
|
||||
<option value="">[级别]</option>
|
||||
<option value="error">错误</option>
|
||||
<option value="warning">警告</option>
|
||||
<option value="info">信息</option>
|
||||
<option value="success">成功</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" style="width:10em" v-model="keyword" placeholder="关键词"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
</div>
|
||||
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||
<a :href="'/httpdns/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
|
||||
|
||||
<table class="ui table selectable" v-if="logs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="log in logs">
|
||||
<td>
|
||||
<node-log-row :v-log="log" :v-keyword="keyword"></node-log-row>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
@@ -0,0 +1,6 @@
|
||||
Tea.context(function () {
|
||||
this.$delay(function () {
|
||||
teaweb.datepicker("day-from-picker")
|
||||
teaweb.datepicker("day-to-picker")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
pre.log-box {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{$layout}
|
||||
|
||||
{$template "node_menu"}
|
||||
|
||||
<h3>修改节点</h3>
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="nodeId" :value="node.id" />
|
||||
<input type="hidden" name="loginId" :value="loginId" />
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="node.name" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP地址 *</td>
|
||||
<td>
|
||||
<node-ip-addresses-box role="ns" :v-ip-addresses="node.ipAddresses"></node-ip-addresses-box>
|
||||
<p class="comment">用于访问节点和处理HTTPDNS解析请求等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>所属集群</td>
|
||||
<td>
|
||||
<select class="ui dropdown" name="clusterId" style="width:10em" v-model="clusterId">
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>SSH主机地址</td>
|
||||
<td>
|
||||
<input type="text" name="sshHost" maxlength="64" v-model="sshHost" />
|
||||
<p class="comment">比如192.168.1.100</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH主机端口</td>
|
||||
<td>
|
||||
<input type="text" name="sshPort" maxlength="5" v-model="sshPort" />
|
||||
<p class="comment">比如22。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH登录认证</td>
|
||||
<td>
|
||||
<grant-selector :v-grant="grant" :v-node-cluster-id="clusterId"></grant-selector>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">API节点地址</td>
|
||||
<td>
|
||||
<div style="margin-bottom: 0.5em">
|
||||
<api-node-addresses-box :v-name="'apiNodeAddrsJSON'"
|
||||
:v-addrs="apiNodeAddrs"></api-node-addresses-box>
|
||||
</div>
|
||||
<p class="comment">当前节点单独使用的API节点设置。<pro-warning-label></pro-warning-label></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用当前节点</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="node.isOn" />
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">如果不启用此节点,此节点上的所有网站将不能访问。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,35 @@
|
||||
Tea.context(function () {
|
||||
this.clusterId = 0;
|
||||
if (this.node.cluster != null && this.node.cluster.id > 0) {
|
||||
this.clusterId = this.node.cluster.id;
|
||||
}
|
||||
|
||||
this.success = NotifySuccess("保存成功", "/httpdns/clusters/cluster/node?clusterId=" + this.clusterId + "&nodeId=" + this.node.id);
|
||||
|
||||
// 认证相关
|
||||
this.grant = null
|
||||
|
||||
this.sshHost = ""
|
||||
this.sshPort = ""
|
||||
this.loginId = 0
|
||||
if (this.node.login != null) {
|
||||
this.loginId = this.node.login.id
|
||||
|
||||
if (this.node.login.params != null) {
|
||||
this.sshHost = this.node.login.params.host
|
||||
if (this.node.login.params.port > 0) {
|
||||
this.sshPort = this.node.login.params.port
|
||||
}
|
||||
}
|
||||
|
||||
if (this.node.login.grant != null && typeof this.node.login.grant.id != "undefined") {
|
||||
this.grant = {
|
||||
id: this.node.login.grant.id,
|
||||
name: this.node.login.grant.name,
|
||||
method: this.node.login.grant.method,
|
||||
methodName: this.node.login.grant.methodName,
|
||||
username: this.node.login.grant.username
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,71 @@
|
||||
{$layout}
|
||||
{$template "cluster_menu"}
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<h3 class="ui header">集群设置</h3>
|
||||
<p><strong>{{cluster.name}}</strong></p>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="clusterId" :value="cluster.id" />
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">集群名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" v-model="cluster.name" />
|
||||
<p class="comment">用于区分不同环境的解析节点池。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>集群服务域名 *</td>
|
||||
<td>
|
||||
<input type="text" name="gatewayDomain" maxlength="255" v-model="settings.gatewayDomain" placeholder="例如 gw-hz.httpdns.example.com" />
|
||||
<p class="comment">该集群下应用用于 SDK 接入的 HTTPDNS 服务域名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"><more-options-indicator></more-options-indicator></td>
|
||||
</tr>
|
||||
<tbody v-show="moreOptionsVisible">
|
||||
<tr>
|
||||
<td>降级超时容忍度</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" name="fallbackTimeout" maxlength="5" v-model="settings.fallbackTimeout"
|
||||
style="width: 6em" />
|
||||
<span class="ui label">毫秒</span>
|
||||
</div>
|
||||
<p class="comment">HTTPDNS 网关请求源站超时多长时间后,强制降级返回缓存兜底IP(保障 P99 响应延迟)。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>本地内存缓存</td>
|
||||
<td>
|
||||
<div class="ui input right labeled">
|
||||
<input type="text" name="cacheTtl" maxlength="5" v-model="settings.cacheTtl"
|
||||
style="width: 6em" />
|
||||
<span class="ui label">秒</span>
|
||||
</div>
|
||||
<p class="comment">在网关节点内存中缓存解析结果的时长,缓解峰值查询压力。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>节点安装根目录</td>
|
||||
<td>
|
||||
<input type="text" name="installDir" maxlength="100" v-model="settings.installDir" />
|
||||
<p class="comment">此集群下新加网关节点的主程序默认部署路径。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>启用当前集群</td>
|
||||
<td>
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="isOn" value="1" v-model="settings.isOn" />
|
||||
<label></label>
|
||||
</div>
|
||||
<p class="comment">如果取消启用,此集群下的所有 HTTPDNS 网关节点将停止处理解析请求。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,3 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyReloadSuccess("保存成功");
|
||||
});
|
||||
23
EdgeAdmin/web/views/@default/httpdns/clusters/create.html
Normal file
23
EdgeAdmin/web/views/@default/httpdns/clusters/create.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<div class="ui message info">目前这是一个纯前端占位页面(Mock),后续将对接真实的后端 API。</div>
|
||||
|
||||
<form class="ui form">
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">集群名称 *</td>
|
||||
<td>
|
||||
<input type="text" placeholder="比如 gateway-cn-hz" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>集群服务域名 *</td>
|
||||
<td>
|
||||
<input type="text" placeholder="比如 gw-hz.httpdns.example.com" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="button" class="ui button primary">保存</button>
|
||||
</form>
|
||||
@@ -0,0 +1,7 @@
|
||||
.left-box {
|
||||
top: 10em;
|
||||
}
|
||||
.right-box {
|
||||
top: 10em;
|
||||
}
|
||||
/*# sourceMappingURL=createNode.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["createNode.less"],"names":[],"mappings":"AAAA;EACC,SAAA;;AAGD;EACC,SAAA","file":"createNode.css"}
|
||||
@@ -0,0 +1,22 @@
|
||||
{$layout}
|
||||
{$template "cluster_menu"}
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="clusterId" :value="clusterId" />
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">节点名称 *</td>
|
||||
<td>
|
||||
<input type="text" name="name" maxlength="50" ref="focus" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP地址 *</td>
|
||||
<td>
|
||||
<node-ip-addresses-box role="ns"></node-ip-addresses-box>
|
||||
<p class="comment">用于访问节点和处理HTTPDNS解析请求等。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,3 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifySuccess("保存成功", "/httpdns/clusters/cluster?clusterId=" + this.clusterId);
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
.left-box {
|
||||
top: 10em;
|
||||
}
|
||||
|
||||
.right-box {
|
||||
top: 10em;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{$layout}
|
||||
{$template "cluster_menu"}
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<div class="buttons-box">
|
||||
<button class="ui button red" type="button" @click.prevent="deleteCluster(cluster.id)">删除当前集群</button>
|
||||
</div>
|
||||
16
EdgeAdmin/web/views/@default/httpdns/clusters/delete.js
Normal file
16
EdgeAdmin/web/views/@default/httpdns/clusters/delete.js
Normal file
@@ -0,0 +1,16 @@
|
||||
Tea.context(function () {
|
||||
this.deleteCluster = function (clusterId) {
|
||||
let that = this
|
||||
teaweb.confirm("确定要删除此集群吗?", function () {
|
||||
that.$post("/httpdns/clusters/delete")
|
||||
.params({
|
||||
clusterId: clusterId
|
||||
})
|
||||
.success(function () {
|
||||
teaweb.success("删除成功", function () {
|
||||
window.location = "/httpdns/clusters"
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
68
EdgeAdmin/web/views/@default/httpdns/clusters/index.html
Normal file
68
EdgeAdmin/web/views/@default/httpdns/clusters/index.html
Normal file
@@ -0,0 +1,68 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div>
|
||||
<div v-if="hasErrorLogs" class="ui icon message small error">
|
||||
<i class="icon warning circle"></i>
|
||||
<div class="content">有部分集群节点状态异常,请及时处理。</div>
|
||||
</div>
|
||||
|
||||
<form method="get" class="ui form" action="/httpdns/clusters">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" style="width:14em" placeholder="集群名称..." v-model="keyword" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
<a href="/httpdns/clusters" v-if="keyword.length > 0">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="ui message" v-if="clusters.length == 0">暂时还没有 HTTPDNS 集群,现在去 <a
|
||||
href="/httpdns/clusters/create">[创建新集群]</a>。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="clusters.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>集群名称</th>
|
||||
<th>服务域名</th>
|
||||
<th class="center width10">节点数</th>
|
||||
<th class="center width10">在线节点数</th>
|
||||
<th class="width5">状态</th>
|
||||
<th class="two op">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="cluster in clusters">
|
||||
<td>
|
||||
<a :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">
|
||||
<keyword :v-word="keyword">{{cluster.name}}</keyword>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{cluster.gatewayDomain}}</code>
|
||||
</td>
|
||||
<td class="center">
|
||||
<a :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id" v-if="cluster.countAllNodes > 0">
|
||||
<span
|
||||
:class="{red:cluster.countAllNodes > cluster.countActiveNodes}">{{cluster.countAllNodes}}</span>
|
||||
</a>
|
||||
<span class="disabled" v-else="">-</span>
|
||||
</td>
|
||||
<td class="center">
|
||||
<a :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id" v-if="cluster.countActiveNodes > 0">
|
||||
<span class="green">{{cluster.countActiveNodes}}</span>
|
||||
</a>
|
||||
<span class="disabled" v-else>-</span>
|
||||
</td>
|
||||
<td><label-on :v-is-on="cluster.isOn"></label-on></td>
|
||||
<td>
|
||||
<a :href="'/httpdns/clusters/cluster?clusterId=' + cluster.id">详情</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="page" v-html="page"></div>
|
||||
</div>
|
||||
17
EdgeAdmin/web/views/@default/httpdns/clusters/index.js
Normal file
17
EdgeAdmin/web/views/@default/httpdns/clusters/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
Tea.context(function () {
|
||||
this.hasErrorLogs = false; // Mock data
|
||||
if (typeof this.clusters == "undefined") {
|
||||
this.clusters = [];
|
||||
}
|
||||
|
||||
this.deleteCluster = function (clusterId) {
|
||||
let that = this;
|
||||
teaweb.confirm("确定要删除这个集群吗?", function () {
|
||||
that.$post(".delete")
|
||||
.params({
|
||||
clusterId: clusterId
|
||||
})
|
||||
.refresh();
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>淇敼鑺傜偣"{{node.name}}"鐨凷SH鐧诲綍淇℃伅</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<input type="hidden" name="nodeId" :value="node.id"/>
|
||||
<input type="hidden" name="loginId" :value="loginId"/>
|
||||
<table class="ui table definition">
|
||||
<tr>
|
||||
<td class="title">SSH涓绘満鍦板潃 *</td>
|
||||
<td>
|
||||
<input type="text" name="sshHost" maxlength="64" v-model="params.host" ref="focus"/>
|
||||
<p class="comment">姣斿192.168.1.100</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH涓绘満绔彛 *</td>
|
||||
<td>
|
||||
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
|
||||
<p class="comment">姣斿22銆?/p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSH鐧诲綍璁よ瘉 *</td>
|
||||
<td>
|
||||
<grant-selector :v-grant="grant" :v-node-cluster-id="clusterId"></grant-selector>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
@@ -0,0 +1,8 @@
|
||||
Tea.context(function () {
|
||||
if (typeof this.params == "undefined" || this.params == null) {
|
||||
this.params = {host: "", port: 22}
|
||||
}
|
||||
if (this.params.port <= 0) {
|
||||
this.params.port = 22
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{$layout}
|
||||
|
||||
<p class="ui message error">姝ゅ姛鑳芥殏鏈紑鏀俱€?/p>
|
||||
4
EdgeAdmin/web/views/@default/httpdns/ech/@menu.html
Normal file
4
EdgeAdmin/web/views/@default/httpdns/ech/@menu.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/ech" code="index">ECH 控制台</menu-item>
|
||||
</first-menu>
|
||||
26
EdgeAdmin/web/views/@default/httpdns/ech/audit.html
Normal file
26
EdgeAdmin/web/views/@default/httpdns/ech/audit.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
{$template "/left_menu_with_menu"}
|
||||
|
||||
<div class="right-box with-menu">
|
||||
<h3 class="ui header">ECH Degrade Audit Logs</h3>
|
||||
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Scope</th>
|
||||
<th>Operator</th>
|
||||
<th>Result</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in auditLogs">
|
||||
<td>{{item.scope}}</td>
|
||||
<td>{{item.operator}}</td>
|
||||
<td>{{item.result}}</td>
|
||||
<td>{{item.createdAt}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
69
EdgeAdmin/web/views/@default/httpdns/ech/index.html
Normal file
69
EdgeAdmin/web/views/@default/httpdns/ech/index.html
Normal file
@@ -0,0 +1,69 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<div>
|
||||
<div class="ui grid">
|
||||
<div class="eight wide column">
|
||||
<div class="ui segment center aligned"
|
||||
:class="{'green inverted': health.keySyncRate >= 0.99, 'red inverted': health.keySyncRate < 0.99}">
|
||||
<h2>全网私钥热加载成功率</h2>
|
||||
<div style="font-size: 3em; font-weight: bold; margin-bottom: 0.2rem;">{{ (health.keySyncRate *
|
||||
100).toFixed(2) }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui segment center aligned"
|
||||
:class="{'green inverted': health.decryptFailRate <= 0.001, 'red inverted': health.decryptFailRate > 0.001}">
|
||||
<h2>全网 ECH 解密失败阻断率</h2>
|
||||
<div style="font-size: 3em; font-weight: bold; margin-bottom: 0.2rem;">{{ (health.decryptFailRate *
|
||||
100).toFixed(4) }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<div class="ui menu text blue">
|
||||
<a href="/httpdns/ech" class="item active">生产证书轮换日志</a>
|
||||
<a href="/httpdns/ech/audit" class="item">降级操作审计</a>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<table class="ui table selectable celled" v-if="echLogs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>发行版本号</th>
|
||||
<th>记录分发类型</th>
|
||||
<th>公钥</th>
|
||||
<th>发布时间</th>
|
||||
<th class="three wide">边缘扩散状态</th>
|
||||
<th class="twz op">受控回落通道</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="log in echLogs">
|
||||
<td><strong>{{log.version}}</strong><span v-if="log.isCurrent"
|
||||
class="ui label tiny blue margin-left-sm">当前生效</span></td>
|
||||
<td><span class="ui label tiny basic">{{log.recordType}}</span></td>
|
||||
<td>
|
||||
<div style="word-break: break-all; font-family: monospace; font-size: 0.9em; color:#666;"
|
||||
:title="log.publicKey">
|
||||
{{log.publicKey.substring(0,30)}}...{{log.publicKey.substring(log.publicKey.length-10)}}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{log.publishTime}}</td>
|
||||
<td>
|
||||
<span v-if="log.syncStatus === 'success'" class="green">100% 同步</span>
|
||||
<span v-else-if="log.syncStatus === 'syncing'" class="orange">正在扩散到 {{log.nodesPending}}
|
||||
节点</span>
|
||||
<span v-else-if="log.syncStatus === 'timeout'" class="red">部分节点超时</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" @click.prevent="openMfaRollback(log.id)" v-if="log.isCurrent" style="color:red">回滚降级
|
||||
(MFA验证)</a>
|
||||
<span class="disabled" v-else>-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
20
EdgeAdmin/web/views/@default/httpdns/ech/index.js
Normal file
20
EdgeAdmin/web/views/@default/httpdns/ech/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
Tea.context(function () {
|
||||
if (typeof this.health == "undefined") {
|
||||
this.health = {
|
||||
keySyncRate: 1.0,
|
||||
decryptFailRate: 0.0
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof this.echLogs == "undefined") {
|
||||
this.echLogs = [];
|
||||
}
|
||||
|
||||
this.openMfaRollback = function (logId) {
|
||||
teaweb.popup("/httpdns/ech/rollbackMfaPopup?logId=" + logId, {
|
||||
height: "26em",
|
||||
width: "48em",
|
||||
title: "全域安全受控降级告警:双人MFA授权"
|
||||
})
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
{$layout "layout_popup"}
|
||||
|
||||
<h3>受控 ECH 回滚降级</h3>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$">
|
||||
<input type="hidden" name="logId" :value="logId" />
|
||||
<csrf-token></csrf-token>
|
||||
<table class="ui table definition selectable">
|
||||
<tr>
|
||||
<td class="title">日志编号</td>
|
||||
<td><code>{{logId}}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">回滚原因 *</td>
|
||||
<td><textarea name="reason" rows="3" ref="focus"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">审批人 OTP 验证码 #1 *</td>
|
||||
<td><input type="text" name="otpCode1" maxlength="16" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">审批人 OTP 验证码 #2 *</td>
|
||||
<td><input type="text" name="otpCode2" maxlength="16" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<submit-btn></submit-btn>
|
||||
</form>
|
||||
4
EdgeAdmin/web/views/@default/httpdns/guide/@menu.html
Normal file
4
EdgeAdmin/web/views/@default/httpdns/guide/@menu.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/guide" code="index">SDK接入向导</menu-item>
|
||||
</first-menu>
|
||||
155
EdgeAdmin/web/views/@default/httpdns/guide/index.html
Normal file
155
EdgeAdmin/web/views/@default/httpdns/guide/index.html
Normal file
@@ -0,0 +1,155 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<style>
|
||||
.httpdns-mini-action {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
margin-left: .55em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.httpdns-mini-action:hover {
|
||||
color: #1e70bf;
|
||||
}
|
||||
.httpdns-mini-action .icon {
|
||||
margin-right: 0 !important;
|
||||
font-size: .92em !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<div class="ui form" style="margin-bottom: 1.5em;">
|
||||
<div class="ui fields inline">
|
||||
<div class="field">
|
||||
<label style="font-weight: 600;">目标应用</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<select class="ui dropdown auto-width" v-model="selectedAppId" @change="onAppChange">
|
||||
<option value="">[请选择应用]</option>
|
||||
<option v-for="app in apps" :value="app.appId">{{app.name}} ({{app.appId}})</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="selectedAppId.length == 0" class="ui segment center aligned" style="padding:4em 1em; color:#999;">
|
||||
<i class="icon cogs" style="font-size:3em;"></i>
|
||||
<p style="margin-top:1em; font-size:1.1em;">请先选择应用,然后查看配置并完成 SDK 接入。</p>
|
||||
</div>
|
||||
|
||||
<div v-show="selectedAppId.length > 0">
|
||||
<div class="ui two steps mini" style="margin-bottom: 0;">
|
||||
<a class="step" :class="{active: currentStep == 1}" @click.prevent="currentStep=1">
|
||||
<i class="icon file alternate outline"></i>
|
||||
<div class="content">
|
||||
<div class="title">01 查看配置</div>
|
||||
<div class="description">查看 SDK 初始化参数</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="step" :class="{active: currentStep == 2}" @click.prevent="currentStep=2">
|
||||
<i class="icon code"></i>
|
||||
<div class="content">
|
||||
<div class="title">02 开发接入</div>
|
||||
<div class="description">下载 SDK 并集成项目</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui segment" v-show="currentStep == 1"
|
||||
style="border-top:none; margin-top:0; border-top-left-radius:0; border-top-right-radius:0;">
|
||||
<h4 class="ui header">查看配置</h4>
|
||||
<table class="ui table definition">
|
||||
<tr>
|
||||
<td class="four wide">App ID</td>
|
||||
<td>
|
||||
<code>{{selectedApp.appId}}</code>
|
||||
<a href="" class="httpdns-mini-action" title="复制 App ID" @click.prevent="copyText(selectedApp.appId, 'App ID')"><i class="copy outline icon"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>应用名称</td>
|
||||
<td><strong>{{selectedApp.name}}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>集群服务地址</td>
|
||||
<td>
|
||||
<code>{{selectedApp.gatewayDomain}}</code>
|
||||
<a href="" class="httpdns-mini-action" title="复制服务地址" @click.prevent="copyText(selectedApp.gatewayDomain, '服务地址')"><i class="copy outline icon"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>加签 Secret</td>
|
||||
<td>
|
||||
<code>{{signSecretVisible ? selectedApp.signSecret : selectedApp.signSecretMasked}}</code>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="signSecretVisible = !signSecretVisible" :title="signSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="signSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||
<a href="" class="httpdns-mini-action" title="复制加签 Secret" @click.prevent="copyText(selectedApp.signSecret, '加签 Secret')"><i class="copy outline icon"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AES 数据加密 Secret</td>
|
||||
<td>
|
||||
<code>{{aesSecretVisible ? selectedApp.aesSecret : selectedApp.aesSecretMasked}}</code>
|
||||
<a href="" class="httpdns-mini-action" @click.prevent="aesSecretVisible = !aesSecretVisible" :title="aesSecretVisible ? '隐藏明文' : '查看明文'"><i class="icon" :class="aesSecretVisible ? 'eye slash' : 'eye'"></i></a>
|
||||
<a href="" class="httpdns-mini-action" title="复制 AES 数据加密 Secret" @click.prevent="copyText(selectedApp.aesSecret, 'AES Secret')"><i class="copy outline icon"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>请求验签</td>
|
||||
<td>
|
||||
<span class="green" v-if="selectedApp.signEnabled">已开启</span>
|
||||
<span class="grey" v-else>已关闭</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="" class="ui button small" @click.prevent="currentStep=2">
|
||||
下一步 <i class="icon arrow right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="ui segment" v-show="currentStep == 2"
|
||||
style="border-top:none; margin-top:0; border-top-left-radius:0; border-top-right-radius:0;">
|
||||
<h4 class="ui header">开发接入</h4>
|
||||
<p class="grey">选择对应平台 SDK 下载并查阅集成文档。</p>
|
||||
|
||||
<div class="ui three cards" style="margin-top: 1.5em;">
|
||||
<div class="card">
|
||||
<div class="content">
|
||||
<div class="header"><i class="icon android green"></i> Android SDK</div>
|
||||
<div class="description" style="margin-top:.5em;">
|
||||
适用于 Android 5.0+ 的原生 SDK,支持 Java / Kotlin。
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<a class="ui button primary mini"><i class="icon download"></i> 下载 SDK</a>
|
||||
<a class="ui button basic mini"><i class="icon book"></i> 集成帮助文档</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="content">
|
||||
<div class="header"><i class="icon apple grey"></i> iOS SDK</div>
|
||||
<div class="description" style="margin-top:.5em;">
|
||||
适用于 iOS 12+ 的原生 SDK,支持 Swift / Objective-C。
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<a class="ui button primary mini"><i class="icon download"></i> 下载 SDK</a>
|
||||
<a class="ui button basic mini"><i class="icon book"></i> 集成帮助文档</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="content">
|
||||
<div class="header"><i class="icon mobile alternate blue"></i> Flutter SDK</div>
|
||||
<div class="description" style="margin-top:.5em;">
|
||||
跨平台 Flutter 插件,同时支持 Android 和 iOS。
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra content">
|
||||
<a class="ui button primary mini"><i class="icon download"></i> 下载 SDK</a>
|
||||
<a class="ui button basic mini"><i class="icon book"></i> 集成帮助文档</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
75
EdgeAdmin/web/views/@default/httpdns/guide/index.js
Normal file
75
EdgeAdmin/web/views/@default/httpdns/guide/index.js
Normal file
@@ -0,0 +1,75 @@
|
||||
Tea.context(function () {
|
||||
this.selectedAppId = ""
|
||||
this.selectedApp = {}
|
||||
this.currentStep = 1
|
||||
this.signSecretVisible = false
|
||||
this.aesSecretVisible = false
|
||||
|
||||
if (typeof this.apps == "undefined") {
|
||||
this.apps = []
|
||||
}
|
||||
|
||||
this.onAppChange = function () {
|
||||
if (this.selectedAppId.length == 0) {
|
||||
this.selectedApp = {}
|
||||
return
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.apps.length; i++) {
|
||||
if (this.apps[i].appId == this.selectedAppId) {
|
||||
this.selectedApp = this.apps[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!this.selectedApp.gatewayDomain || this.selectedApp.gatewayDomain.length == 0) {
|
||||
this.selectedApp.gatewayDomain = "gw.httpdns.example.com"
|
||||
}
|
||||
|
||||
this.signSecretVisible = false
|
||||
this.aesSecretVisible = false
|
||||
this.currentStep = 1
|
||||
}
|
||||
|
||||
this.copyText = function (text, name) {
|
||||
if (typeof text != "string" || text.length == 0) {
|
||||
teaweb.warn("没有可复制的内容")
|
||||
return
|
||||
}
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
teaweb.success(name + "已复制")
|
||||
}).catch(function () {
|
||||
this.copyByTextarea(text, name)
|
||||
}.bind(this))
|
||||
return
|
||||
}
|
||||
|
||||
this.copyByTextarea(text, name)
|
||||
}
|
||||
|
||||
this.copyByTextarea = function (text, name) {
|
||||
var input = document.createElement("textarea")
|
||||
input.value = text
|
||||
input.setAttribute("readonly", "readonly")
|
||||
input.style.position = "fixed"
|
||||
input.style.left = "-10000px"
|
||||
input.style.top = "-10000px"
|
||||
document.body.appendChild(input)
|
||||
input.select()
|
||||
|
||||
var ok = false
|
||||
try {
|
||||
ok = document.execCommand("copy")
|
||||
} catch (e) {
|
||||
ok = false
|
||||
}
|
||||
document.body.removeChild(input)
|
||||
|
||||
if (ok) {
|
||||
teaweb.success(name + "已复制")
|
||||
} else {
|
||||
teaweb.warn("复制失败,请手动复制")
|
||||
}
|
||||
}
|
||||
})
|
||||
3
EdgeAdmin/web/views/@default/httpdns/policies/@menu.html
Normal file
3
EdgeAdmin/web/views/@default/httpdns/policies/@menu.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/policies" code="index">全局策略</menu-item>
|
||||
</first-menu>
|
||||
121
EdgeAdmin/web/views/@default/httpdns/policies/index.html
Normal file
121
EdgeAdmin/web/views/@default/httpdns/policies/index.html
Normal file
@@ -0,0 +1,121 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<style>
|
||||
.httpdns-policy-grid {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.httpdns-policy-grid .left-nav-column {
|
||||
max-width: 220px;
|
||||
}
|
||||
.httpdns-policy-grid .right-form-column {
|
||||
padding-left: .3em !important;
|
||||
}
|
||||
.httpdns-policy-menu .item {
|
||||
padding-top: .8em !important;
|
||||
padding-bottom: .8em !important;
|
||||
}
|
||||
.httpdns-policy-note.comment {
|
||||
color: #8f9aa6 !important;
|
||||
font-size: 12px;
|
||||
margin-top: .45em !important;
|
||||
}
|
||||
.httpdns-inline-field {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .45em;
|
||||
}
|
||||
.httpdns-inline-field input {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.httpdns-inline-field .httpdns-unit {
|
||||
color: #333;
|
||||
min-width: 2em;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
.httpdns-inline-hint {
|
||||
color: #8f9aa6;
|
||||
margin-left: .35em;
|
||||
}
|
||||
.httpdns-unit-input {
|
||||
display: inline-flex !important;
|
||||
align-items: stretch !important;
|
||||
}
|
||||
.httpdns-unit-input .label {
|
||||
min-width: 3em;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="ui margin"></div>
|
||||
|
||||
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
|
||||
<csrf-token></csrf-token>
|
||||
|
||||
<div class="ui stackable grid httpdns-policy-grid">
|
||||
<div class="three wide computer four wide tablet sixteen wide mobile column left-nav-column">
|
||||
<div class="ui fluid vertical pointing menu httpdns-policy-menu">
|
||||
<a href="" class="item" :class="{active: activeSection == 'user'}" @click.prevent="activeSection='user'">用户设置</a>
|
||||
<a href="" class="item" :class="{active: activeSection == 'basic'}" @click.prevent="activeSection='basic'">基础默认</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="thirteen wide computer twelve wide tablet sixteen wide mobile column right-form-column">
|
||||
<table class="ui table definition selectable" v-show="activeSection == 'user'">
|
||||
<tr>
|
||||
<td class="title">默认部署集群</td>
|
||||
<td>
|
||||
<select name="defaultClusterId" class="ui dropdown auto-width" v-model="policies.defaultClusterId">
|
||||
<option v-for="cluster in availableClusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
<p class="comment httpdns-policy-note">用户新建应用时默认落到此集群。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">启用用户域名校验</td>
|
||||
<td>
|
||||
<checkbox name="enableUserDomainVerify" value="1" v-model="policies.enableUserDomainVerify"></checkbox>
|
||||
<p class="comment httpdns-policy-note">开启后,用户添加域名需要通过归属校验。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="ui table definition selectable" v-show="activeSection == 'basic'">
|
||||
<tr>
|
||||
<td class="title">SNI 防护配置</td>
|
||||
<td>
|
||||
<span class="green">隐匿 SNI</span>
|
||||
<p class="comment httpdns-policy-note">当前统一采用隐匿 SNI 策略,避免在握手阶段暴露业务域名。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">全局默认解析 TTL</td>
|
||||
<td>
|
||||
<div class="httpdns-inline-field">
|
||||
<div class="ui right labeled input httpdns-unit-input" style="width:11em;">
|
||||
<input type="text" name="defaultTTL" maxlength="8" v-model="policies.defaultTTL" />
|
||||
<div class="ui basic label">秒</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment httpdns-policy-note">建议 30~120 秒,兼顾缓存命中与切换速度。</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">全局降级超时</td>
|
||||
<td>
|
||||
<div class="httpdns-inline-field">
|
||||
<div class="ui right labeled input httpdns-unit-input" style="width:11em;">
|
||||
<input type="text" name="defaultFallbackMs" maxlength="8" v-model="policies.defaultFallbackMs" />
|
||||
<div class="ui basic label">毫秒</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="comment httpdns-policy-note">超时后可走降级解析流程,建议 200~800ms。</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<submit-btn></submit-btn>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
22
EdgeAdmin/web/views/@default/httpdns/policies/index.js
Normal file
22
EdgeAdmin/web/views/@default/httpdns/policies/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
Tea.context(function () {
|
||||
this.success = NotifyReloadSuccess("保存成功");
|
||||
this.activeSection = "user";
|
||||
|
||||
if (!Array.isArray(this.availableClusters)) {
|
||||
this.availableClusters = [];
|
||||
}
|
||||
if (!this.policies.defaultClusterId || this.policies.defaultClusterId <= 0) {
|
||||
if (this.availableClusters.length > 0) {
|
||||
this.policies.defaultClusterId = this.availableClusters[0].id;
|
||||
}
|
||||
}
|
||||
if (typeof this.policies.enableUserDomainVerify == "undefined") {
|
||||
this.policies.enableUserDomainVerify = true;
|
||||
}
|
||||
if (typeof this.policies.defaultTTL == "undefined" || this.policies.defaultTTL <= 0) {
|
||||
this.policies.defaultTTL = 30;
|
||||
}
|
||||
if (typeof this.policies.defaultFallbackMs == "undefined" || this.policies.defaultFallbackMs <= 0) {
|
||||
this.policies.defaultFallbackMs = 300;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/resolveLogs" code="index">解析日志</menu-item>
|
||||
</first-menu>
|
||||
94
EdgeAdmin/web/views/@default/httpdns/resolveLogs/index.html
Normal file
94
EdgeAdmin/web/views/@default/httpdns/resolveLogs/index.html
Normal file
@@ -0,0 +1,94 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
|
||||
<style>
|
||||
.httpdns-log-summary {
|
||||
color: #556070;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.httpdns-log-summary code {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form method="get" action="/httpdns/resolveLogs" class="ui form" autocomplete="off">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
||||
<option value="">[集群]</option>
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
||||
<option value="">[节点]</option>
|
||||
<option v-for="node in nodes" :value="node.id" v-if="clusterId == '' || clusterId == node.clusterId">{{node.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="appId" v-model="appId" placeholder="AppID" style="width:10em" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="domain" v-model="domain" placeholder="域名" style="width:13em" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="status" v-model="status">
|
||||
<option value="">[状态]</option>
|
||||
<option value="success">解析成功</option>
|
||||
<option value="failed">解析失败</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" v-model="keyword" placeholder="应用/域名/IP/结果IP" style="width:14em" />
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
</div>
|
||||
<div class="ui field" v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || appId.length > 0 || domain.length > 0 || status.length > 0 || keyword.length > 0">
|
||||
<a href="/httpdns/resolveLogs">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="resolveLogs.length == 0">暂时还没有解析日志。</p>
|
||||
|
||||
<div style="overflow-x:auto;" v-if="resolveLogs.length > 0">
|
||||
<table class="ui table selectable celled">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>集群</th>
|
||||
<th>节点</th>
|
||||
<th>域名</th>
|
||||
<th>类型</th>
|
||||
<th>概要</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="log in resolveLogs">
|
||||
<td>{{log.clusterName}}</td>
|
||||
<td>{{log.nodeName}}</td>
|
||||
<td>{{log.domain}}</td>
|
||||
<td>{{log.query}}</td>
|
||||
<td>
|
||||
<div class="httpdns-log-summary">
|
||||
{{log.time}}
|
||||
| {{log.appName}}(<code>{{log.appId}}</code>)
|
||||
| <code>{{log.clientIp}}</code>
|
||||
| {{log.os}}/{{log.sdkVersion}}
|
||||
| {{log.query}} {{log.domain}} ->
|
||||
<code v-if="log.ips && log.ips.length > 0">{{log.ips}}</code>
|
||||
<span class="grey" v-else>[无记录]</span>
|
||||
|
|
||||
<span class="green" v-if="log.status == 'success'"><strong>成功</strong></span>
|
||||
<span class="red" v-else><strong>失败</strong></span>
|
||||
<span class="grey" v-if="log.errorCode != 'none'">({{log.errorCode}})</span>
|
||||
| {{log.costMs}}ms
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/runtimeLogs" code="index">运行日志</menu-item>
|
||||
</first-menu>
|
||||
80
EdgeAdmin/web/views/@default/httpdns/runtimeLogs/index.html
Normal file
80
EdgeAdmin/web/views/@default/httpdns/runtimeLogs/index.html
Normal file
@@ -0,0 +1,80 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
{$template "/datepicker"}
|
||||
|
||||
<form method="get" action="/httpdns/runtimeLogs" class="ui form" autocomplete="off">
|
||||
<div class="ui fields inline">
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="clusterId" v-model="clusterId">
|
||||
<option value="">[集群]</option>
|
||||
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="nodeId" v-model="nodeId">
|
||||
<option value="">[节点]</option>
|
||||
<option v-for="node in nodes" :value="node.id" v-if="clusterId == '' || clusterId == node.clusterId">{{node.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayFrom" placeholder="开始日期" v-model="dayFrom" style="width:8em" id="day-from-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="dayTo" placeholder="结束日期" v-model="dayTo" style="width:8em" id="day-to-picker"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<select class="ui dropdown" name="level" v-model="level">
|
||||
<option value="">[级别]</option>
|
||||
<option value="error">error</option>
|
||||
<option value="warning">warning</option>
|
||||
<option value="info">info</option>
|
||||
<option value="success">success</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<input type="text" name="keyword" style="width:15em" v-model="keyword" placeholder="类型/模块/详情/节点"/>
|
||||
</div>
|
||||
<div class="ui field">
|
||||
<button type="submit" class="ui button">查询</button>
|
||||
</div>
|
||||
<div class="ui field" v-if="clusterId.toString().length > 0 || nodeId.toString().length > 0 || dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
|
||||
<a href="/httpdns/runtimeLogs">[清除条件]</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="comment" v-if="runtimeLogs.length == 0">暂时还没有运行日志。</p>
|
||||
|
||||
<table class="ui table selectable celled" v-if="runtimeLogs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>集群</th>
|
||||
<th>节点</th>
|
||||
<th>级别</th>
|
||||
<th>类型</th>
|
||||
<th>模块</th>
|
||||
<th>详情</th>
|
||||
<th>次数</th>
|
||||
<th>请求ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="log in runtimeLogs">
|
||||
<td>{{log.createdTime}}</td>
|
||||
<td>{{log.clusterName}}</td>
|
||||
<td>{{log.nodeName}}</td>
|
||||
<td>
|
||||
<span class="ui label tiny red" v-if="log.level == 'error'">error</span>
|
||||
<span class="ui label tiny orange" v-else-if="log.level == 'warning'">warning</span>
|
||||
<span class="ui label tiny blue" v-else-if="log.level == 'info'">info</span>
|
||||
<span class="ui label tiny green" v-else>success</span>
|
||||
</td>
|
||||
<td><code>{{log.tag}}</code></td>
|
||||
<td>{{log.module}}</td>
|
||||
<td>{{log.description}}</td>
|
||||
<td>{{log.count}}</td>
|
||||
<td><code>{{log.requestId}}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,6 @@
|
||||
Tea.context(function () {
|
||||
this.$delay(function () {
|
||||
teaweb.datepicker("day-from-picker");
|
||||
teaweb.datepicker("day-to-picker");
|
||||
});
|
||||
});
|
||||
4
EdgeAdmin/web/views/@default/httpdns/sandbox/@menu.html
Normal file
4
EdgeAdmin/web/views/@default/httpdns/sandbox/@menu.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<!-- 左侧菜单由 Go Backend 自动生成注入,此处加首行子菜单使其符合标准平台样式 -->
|
||||
<first-menu>
|
||||
<menu-item href="/httpdns/sandbox" code="index">API 在线沙盒</menu-item>
|
||||
</first-menu>
|
||||
171
EdgeAdmin/web/views/@default/httpdns/sandbox/index.html
Normal file
171
EdgeAdmin/web/views/@default/httpdns/sandbox/index.html
Normal file
@@ -0,0 +1,171 @@
|
||||
{$layout}
|
||||
{$template "menu"}
|
||||
<style>
|
||||
.httpdns-sdns-row {
|
||||
display: flex;
|
||||
gap: .5em;
|
||||
align-items: center;
|
||||
margin-bottom: .45em;
|
||||
}
|
||||
.httpdns-sdns-row .field {
|
||||
margin: 0 !important;
|
||||
flex: 1;
|
||||
}
|
||||
.httpdns-sdns-actions {
|
||||
margin-top: .2em;
|
||||
}
|
||||
.httpdns-sdns-actions .count {
|
||||
color: #8f9aa6;
|
||||
margin-left: .4em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<div class="ui grid stackable">
|
||||
<!-- Left: 解析配置 -->
|
||||
<div class="six wide column">
|
||||
<div class="ui segment">
|
||||
<h4 class="ui header" style="margin-top:0;">解析配置</h4>
|
||||
<div class="ui form">
|
||||
<div class="field">
|
||||
<label>目标应用 *</label>
|
||||
<select class="ui dropdown" name="appId" v-model="request.appId">
|
||||
<option value="">[请选择应用]</option>
|
||||
<option v-for="app in apps" :value="app.appId">{{app.name}} ({{app.appId}})</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>解析域名 *</label>
|
||||
<input type="text" v-model="request.domain" placeholder="例如 www.example.com" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>模拟客户端 IP</label>
|
||||
<input type="text" v-model="request.clientIp" placeholder="留空使用当前 IP" />
|
||||
<p class="comment">用于测试 ECS 掩码与区域调度效果。</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>解析类型</label>
|
||||
<div class="ui buttons mini">
|
||||
<button class="ui button" :class="{primary: request.qtype=='A'}"
|
||||
@click.prevent="request.qtype='A'" type="button">A</button>
|
||||
<button class="ui button" :class="{primary: request.qtype=='AAAA'}"
|
||||
@click.prevent="request.qtype='AAAA'" type="button">AAAA</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>SDNS 参数</label>
|
||||
<div class="httpdns-sdns-row" v-for="(param, index) in sdnsParams">
|
||||
<div class="field">
|
||||
<input type="text" maxlength="64" placeholder="参数名称" v-model="param.name" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="text" maxlength="64" placeholder="参数值" v-model="param.value" />
|
||||
</div>
|
||||
<a href="" @click.prevent="removeSDNSParam(index)" title="删除"><i class="icon trash alternate outline"></i></a>
|
||||
</div>
|
||||
<div class="httpdns-sdns-actions">
|
||||
<a href="" @click.prevent="addSDNSParam" :class="{disabled: sdnsParams.length >= 10}">
|
||||
<i class="icon plus circle"></i>添加参数
|
||||
</a>
|
||||
<span class="count">{{sdnsParams.length}}/10</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<button class="ui button primary" type="button" @click.prevent="sendTestRequest"
|
||||
:class="{loading: isRequesting, disabled: isRequesting}">
|
||||
<i class="icon search"></i> 在线解析
|
||||
</button>
|
||||
<button class="ui button basic" type="button" @click.prevent="resetForm">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: 解析结果 -->
|
||||
<div class="ten wide column">
|
||||
<!-- Empty state -->
|
||||
<div v-show="!response.hasResult && !isRequesting" class="ui segment placeholder">
|
||||
<p class="grey" style="text-align:center;">请在左侧配置参数后点击「在线解析」。</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading -->
|
||||
<div v-show="isRequesting" class="ui segment" style="min-height:200px;">
|
||||
<div class="ui active dimmer">
|
||||
<div class="ui text loader">解析中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div v-show="response.hasResult && !isRequesting">
|
||||
<!-- Status banner -->
|
||||
<div class="ui message small" :class="{positive: response.code === 0, negative: response.code !== 0}">
|
||||
<div class="header">
|
||||
<span v-if="response.code === 0"><i class="icon check circle"></i> 解析成功</span>
|
||||
<span v-else><i class="icon times circle"></i> 解析失败 ({{response.code}})</span>
|
||||
</div>
|
||||
<p v-if="response.code !== 0">{{response.message}}</p>
|
||||
<p class="grey small" style="margin-top:.3em;">Request ID: <code>{{response.requestId}}</code></p>
|
||||
</div>
|
||||
|
||||
<!-- Result details -->
|
||||
<div v-if="response.code === 0 && response.data" style="margin-top: 1em;">
|
||||
<h5 class="ui header">解析结果</h5>
|
||||
<div class="ui segment" style="margin-top:.4em;">
|
||||
<div style="font-weight: 600; margin-bottom: .5em;">请求URL</div>
|
||||
<div style="word-break: break-all;">
|
||||
<code>{{response.data.request_url || '-'}}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="ui header" style="margin-top:1.2em;">解析结果详情</h5>
|
||||
<div class="ui three column stackable grid" style="margin-top:0;">
|
||||
<div class="column">
|
||||
<div class="ui segment">
|
||||
<div class="grey">客户端 IP</div>
|
||||
<div style="margin-top:.4em;"><code>{{response.data.client_ip || request.clientIp || '-'}}</code></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui segment">
|
||||
<div class="grey">地区</div>
|
||||
<div style="margin-top:.4em;">{{response.data.client_region || '-'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui segment">
|
||||
<div class="grey">线路</div>
|
||||
<div style="margin-top:.4em;">{{response.data.line_name || '-'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IP results table -->
|
||||
<h5 class="ui header" style="margin-top:1.2em;">解析记录</h5>
|
||||
<table class="ui table celled compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>解析域名</th>
|
||||
<th>解析类型</th>
|
||||
<th>IP地址</th>
|
||||
<th>TTL</th>
|
||||
<th>地区</th>
|
||||
<th>线路</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, idx) in response.resultRows">
|
||||
<td v-if="idx === 0" :rowspan="response.resultRows.length">{{row.domain || request.domain}}</td>
|
||||
<td>{{row.type || request.qtype}}</td>
|
||||
<td><code>{{row.ip}}</code></td>
|
||||
<td>{{row.ttl}}s</td>
|
||||
<td>{{row.region || '-'}}</td>
|
||||
<td>{{row.line || '-'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
135
EdgeAdmin/web/views/@default/httpdns/sandbox/index.js
Normal file
135
EdgeAdmin/web/views/@default/httpdns/sandbox/index.js
Normal file
@@ -0,0 +1,135 @@
|
||||
Tea.context(function () {
|
||||
this.newRequest = function () {
|
||||
return {
|
||||
appId: "",
|
||||
domain: "",
|
||||
clientIp: "",
|
||||
qtype: "A"
|
||||
}
|
||||
}
|
||||
|
||||
this.request = this.newRequest()
|
||||
this.sdnsParams = [{name: "", value: ""}]
|
||||
|
||||
this.response = {
|
||||
hasResult: false,
|
||||
code: -1,
|
||||
message: "",
|
||||
data: null,
|
||||
requestId: "",
|
||||
resultRows: []
|
||||
}
|
||||
|
||||
this.isRequesting = false
|
||||
|
||||
if (typeof this.apps == "undefined") {
|
||||
this.apps = []
|
||||
}
|
||||
|
||||
this.addSDNSParam = function () {
|
||||
if (this.sdnsParams.length >= 10) {
|
||||
return
|
||||
}
|
||||
this.sdnsParams.push({name: "", value: ""})
|
||||
}
|
||||
|
||||
this.removeSDNSParam = function (index) {
|
||||
if (index < 0 || index >= this.sdnsParams.length) {
|
||||
return
|
||||
}
|
||||
this.sdnsParams.splice(index, 1)
|
||||
if (this.sdnsParams.length == 0) {
|
||||
this.sdnsParams.push({name: "", value: ""})
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanSDNSParams = function () {
|
||||
let list = []
|
||||
this.sdnsParams.forEach(function (item) {
|
||||
let name = (item.name || "").trim()
|
||||
let value = (item.value || "").trim()
|
||||
if (name.length == 0 && value.length == 0) {
|
||||
return
|
||||
}
|
||||
list.push({
|
||||
name: name,
|
||||
value: value
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
this.normalizeResultRows = function (data) {
|
||||
if (typeof data == "undefined" || data == null) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (Array.isArray(data.records) && data.records.length > 0) {
|
||||
return data.records
|
||||
}
|
||||
|
||||
let rows = []
|
||||
let ips = []
|
||||
if (Array.isArray(data.ips)) {
|
||||
ips = data.ips
|
||||
}
|
||||
let domain = this.request.domain
|
||||
let qtype = this.request.qtype
|
||||
let ttl = data.ttl || 0
|
||||
let region = data.client_region || "-"
|
||||
let line = data.line_name || "-"
|
||||
|
||||
ips.forEach(function (ip) {
|
||||
rows.push({
|
||||
domain: domain,
|
||||
type: qtype,
|
||||
ip: ip,
|
||||
ttl: ttl,
|
||||
region: region,
|
||||
line: line
|
||||
})
|
||||
})
|
||||
return rows
|
||||
}
|
||||
|
||||
this.sendTestRequest = function () {
|
||||
if (this.request.appId.length == 0) {
|
||||
teaweb.warn("请选择目标应用")
|
||||
return
|
||||
}
|
||||
if (this.request.domain.length == 0) {
|
||||
teaweb.warn("请填写解析域名")
|
||||
return
|
||||
}
|
||||
|
||||
this.isRequesting = true
|
||||
this.response.hasResult = false
|
||||
|
||||
let payload = Object.assign({}, this.request)
|
||||
payload.sdnsParamsJSON = JSON.stringify(this.cleanSDNSParams())
|
||||
|
||||
this.$post("/httpdns/sandbox/test")
|
||||
.params(payload)
|
||||
.success(function (resp) {
|
||||
this.response = resp.data.result
|
||||
this.response.hasResult = true
|
||||
this.response.resultRows = this.normalizeResultRows(this.response.data)
|
||||
})
|
||||
.done(function () {
|
||||
this.isRequesting = false
|
||||
})
|
||||
}
|
||||
|
||||
this.resetForm = function () {
|
||||
this.request = this.newRequest()
|
||||
this.sdnsParams = [{name: "", value: ""}]
|
||||
this.response = {
|
||||
hasResult: false,
|
||||
code: -1,
|
||||
message: "",
|
||||
data: null,
|
||||
requestId: "",
|
||||
resultRows: []
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user