管理端全部功能跑通

This commit is contained in:
robin
2026-02-27 10:35:22 +08:00
parent 4d275c921d
commit 150799f41d
263 changed files with 22664 additions and 4053 deletions

View File

@@ -1,4 +1,4 @@
<first-menu>
<menu-item href="/httpdns/apps" code="index">应用列表</menu-item>
<a href="" class="item" @click.prevent="createApp()">[添加应用]</a>
</first-menu>
<a href="/httpdns/apps/create" class="item">[添加应用]</a>
</first-menu>

View File

@@ -1,8 +1,7 @@
{$layout "layout_popup"}
{$layout}
{$template "menu"}
<h3>添加应用</h3>
<form method="post" class="ui form" data-tea-action="$">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
@@ -35,13 +34,10 @@
<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>
<user-selector></user-selector>
<p class="comment">可以选择当前应用所属的平台用户。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</form>

Binary file not shown.

View File

@@ -10,7 +10,12 @@
teaweb.popup("/httpdns/apps/customRecords/createPopup?appId=" + this.app.id + "&domainId=" + this.domain.id, {
width: "42em",
height: "33em",
title: "新增自定义解析规则"
title: "新增自定义解析规则",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload();
});
}
});
};
@@ -21,7 +26,12 @@
teaweb.popup("/httpdns/apps/customRecords/createPopup?appId=" + this.app.id + "&domainId=" + this.domain.id + "&recordId=" + recordId, {
width: "42em",
height: "33em",
title: "编辑自定义解析规则"
title: "编辑自定义解析规则",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload();
});
}
});
};

View File

@@ -1,4 +1,4 @@
{$layout "layout_popup"}
{$layout "layout_popup"}
<style>
.httpdns-inline-actions {
@@ -37,7 +37,7 @@
<h3 v-if="isEditing">编辑自定义解析规则</h3>
<h3 v-else>新增自定义解析规则</h3>
<form method="post" class="ui form" data-tea-action="$">
<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" />
<input type="hidden" name="domainId" :value="domain.id" />
@@ -55,7 +55,7 @@
<td class="title">规则名称 *</td>
<td>
<input type="text" name="ruleName" maxlength="50" v-model="record.ruleName" ref="focus" />
<p class="comment">例如:上海电信灰度-v2</p>
<p class="comment">例如:上海电信灰度-v2</p>
</td>
</tr>
<tr>
@@ -105,7 +105,7 @@
<div style="margin-bottom: 0.5em;">
<div class="ui checkbox" style="margin-bottom: 0.5em;">
<input type="checkbox" name="weightEnabled" value="1" v-model="record.weightEnabled" />
<label>启权重调度设置</label>
<label>权重调度设置</label>
</div>
</div>
@@ -157,4 +157,4 @@
</table>
<submit-btn></submit-btn>
</form>
</form>

View File

@@ -1,4 +1,4 @@
Tea.context(function () {
Tea.context(function () {
var vm = this;
if (typeof vm.record == "undefined" || vm.record == null) {

View File

@@ -0,0 +1,36 @@
# Android SDK
## 初始化
```java
new InitConfig.Builder()
.setContext(context)
.setPrimaryServiceHost("httpdns-a.example.com")
.setBackupServiceHost("httpdns-b.example.com")
.setServicePort(443)
.setSecretKey("your-sign-secret")
.setEnableHttps(true)
.buildFor("app1f1ndpo9");
```
## 解析
```java
HTTPDNSResult result = httpDnsService.getHttpDnsResultForHostSyncNonBlocking(
"api.business.com",
RequestIpType.auto,
null,
null
);
```
## 官方业务适配器
```java
HttpDnsHttpAdapter adapter = HttpDns.buildHttpClientAdapter(httpDnsService);
HttpDnsAdapterResponse resp = adapter.execute(
new HttpDnsAdapterRequest("GET", "https://api.business.com/v1/ping")
);
```
固定策略IP 直连 + 空 SNI + Host=真实域名,不回退到带 SNI。

View File

@@ -0,0 +1,35 @@
# Flutter SDK
## 初始化
```dart
await AliyunHttpdns.init(
appId: 'app1f1ndpo9',
primaryServiceHost: 'httpdns-a.example.com',
backupServiceHost: 'httpdns-b.example.com',
servicePort: 443,
secretKey: 'your-sign-secret',
);
await AliyunHttpdns.build();
```
## 解析
```dart
final result = await AliyunHttpdns.resolveHostSyncNonBlocking(
'api.business.com',
ipType: 'both',
);
```
## 官方业务适配器
```dart
final adapter = AliyunHttpdns.createHttpAdapter();
final resp = await adapter.request(
Uri.parse('https://api.business.com/v1/ping'),
method: 'GET',
);
```
固定策略IP 直连 + 空 SNI + Host=真实域名,不回退到带 SNI。

View File

@@ -0,0 +1,31 @@
# iOS SDK
## 初始化
```objc
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc]
initWithAppId:@"app1f1ndpo9"
primaryServiceHost:@"httpdns-a.example.com"
backupServiceHost:@"httpdns-b.example.com"
servicePort:443
signSecret:@"your-sign-secret"];
```
## 解析
```objc
[service resolveHost:@"api.business.com" queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
// result.ipv4s / result.ipv6s
}];
```
## 官方业务适配器
```objc
NSURL *url = [NSURL URLWithString:@"https://api.business.com/v1/ping"];
[service requestURL:url method:@"GET" headers:nil body:nil completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
// handle
}];
```
固定策略IP 直连 + 空 SNI + Host=真实域名,不回退到带 SNI。

View File

@@ -36,7 +36,12 @@
teaweb.popup("/httpdns/apps/domains/createPopup?appId=" + this.app.id, {
height: "24em",
width: "46em",
title: "添加域名"
title: "添加域名",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload();
});
}
});
};

View File

@@ -2,8 +2,9 @@
<h3>添加域名</h3>
<form method="post" class="ui form" data-tea-action="$">
<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" />
<table class="ui table definition selectable">
<tr>
<td class="title">域名 *</td>

View File

@@ -54,7 +54,7 @@
<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"><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>

View File

@@ -4,10 +4,6 @@ Tea.context(function () {
}
this.createApp = function () {
teaweb.popup("/httpdns/apps/createPopup", {
height: "26em",
width: "48em",
title: "添加应用"
});
window.location = "/httpdns/apps/create";
};
});

View File

@@ -34,6 +34,12 @@
<div class="item"><strong>SDK 集成</strong></div>
</second-menu>
<div style="margin-top: 1em; text-align: right">
<a class="ui button tiny blue" :href="'/httpdns/apps/sdk/upload?appId=' + app.id">
<i class="icon upload"></i> 上传 SDK/文档
</a>
</div>
<div class="ui three stackable cards httpdns-sdk-cards">
<div class="card">
<div class="content">
@@ -43,8 +49,8 @@
</div>
<div class="extra content">
<div class="httpdns-sdk-actions">
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=android"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=android"><i class="icon book"></i> 下载文档</a>
</div>
</div>
</div>
@@ -57,8 +63,8 @@
</div>
<div class="extra content">
<div class="httpdns-sdk-actions">
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=ios"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=ios"><i class="icon book"></i> 下载文档</a>
</div>
</div>
</div>
@@ -71,8 +77,8 @@
</div>
<div class="extra content">
<div class="httpdns-sdk-actions">
<a class="ui button compact mini basic" href="javascript:;"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="javascript:;"><i class="icon book"></i> 集成文档</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/download?platform=flutter"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button compact mini basic" href="/httpdns/apps/sdk/doc?platform=flutter"><i class="icon book"></i> 下载文档</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,93 @@
{$layout}
<second-menu>
<a class="item" :href="'/httpdns/apps/domains?appId=' + app.id">{{app.name}}</a>
<span class="item disabled" style="padding-left: 0; padding-right: 0">&raquo;</span>
<a class="item" :href="'/httpdns/apps/sdk?appId=' + app.id">SDK 集成</a>
<span class="item disabled" style="padding-left: 0; padding-right: 0">&raquo;</span>
<div class="item"><strong>上传 SDK</strong></div>
</second-menu>
<form method="post"
enctype="multipart/form-data"
class="ui form"
data-tea-action="$"
data-tea-timeout="300"
data-tea-before="beforeUpload"
data-tea-done="doneUpload"
data-tea-success="successUpload">
<csrf-token></csrf-token>
<input type="hidden" name="appId" :value="app.id"/>
<table class="ui table selectable definition">
<tr>
<td class="title">平台 *</td>
<td>
<select name="platform" class="ui dropdown auto-width" v-model="platform">
<option value="android">Android</option>
<option value="ios">iOS</option>
<option value="flutter">Flutter</option>
</select>
</td>
</tr>
<tr>
<td class="title">版本号 *</td>
<td>
<input type="text" name="version" v-model="version" maxlength="32"/>
<p class="comment">默认 `1.0.0`。同平台上传会覆盖“最新版本”下载内容。</p>
</td>
</tr>
<tr>
<td class="title">SDK 包</td>
<td>
<input type="file" name="sdkFile" accept=".zip"/>
<p class="comment">支持 zip 包,例如 `httpdns-sdk-android.zip`。</p>
</td>
</tr>
<tr>
<td class="title">集成文档</td>
<td>
<input type="file" name="docFile" accept=".md,text/markdown"/>
<p class="comment">支持 Markdown 文件(`.md`)。</p>
</td>
</tr>
</table>
<div v-if="isUploading" class="ui message blue">
正在上传,请稍候...
</div>
<submit-btn v-show="!isUploading">上传并生效</submit-btn>
<button v-if="isUploading" class="ui button disabled" type="button">上传中...</button>
<a class="ui button" :href="'/httpdns/apps/sdk?appId=' + app.id">返回</a>
</form>
<h4 style="margin-top: 1.5em">已上传文件</h4>
<table class="ui table selectable celled" v-if="uploadedFiles.length > 0">
<thead>
<tr>
<th>平台</th>
<th>类型</th>
<th>版本</th>
<th>文件名</th>
<th>大小</th>
<th>更新时间</th>
<th class="one wide">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="file in uploadedFiles">
<td>{{file.platform}}</td>
<td>{{file.fileType}}</td>
<td>{{file.version}}</td>
<td>{{file.name}}</td>
<td>{{file.sizeText}}</td>
<td>{{file.updatedAt}}</td>
<td><a href="" @click.prevent="deleteUploadedFile(file.name)">删除</a></td>
</tr>
</tbody>
</table>
<div class="ui message" v-else>
暂无上传记录。
</div>

View File

@@ -0,0 +1,39 @@
Tea.context(function () {
this.platform = "android";
this.version = this.defaultVersion || "1.0.0";
this.isUploading = false;
if (!Array.isArray(this.uploadedFiles)) {
this.uploadedFiles = [];
}
this.beforeUpload = function () {
this.isUploading = true;
};
this.doneUpload = function () {
this.isUploading = false;
};
this.successUpload = function () {
teaweb.success("上传成功", function () {
window.location = "/httpdns/apps/sdk?appId=" + this.app.id;
}.bind(this));
};
this.deleteUploadedFile = function (filename) {
let that = this;
teaweb.confirm("确定要删除文件 " + filename + " 吗?", function () {
that.$post("/httpdns/apps/sdk/upload/delete")
.params({
appId: that.app.id,
filename: filename
})
.success(function () {
teaweb.success("删除成功", function () {
window.location.reload();
});
});
});
};
});

View File

@@ -1,10 +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">&raquo;</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?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>
<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>

View File

@@ -2,17 +2,16 @@
{$template "node_menu"}
{$template "/code_editor"}
<!-- 已安装 -->
<div v-if="node.isInstalled">
<div class="ui message green">当前节点为已安装状态。</div>
<a href="" @click.prevent="updateNodeIsInstalled(false)">[重新安装]</a>
<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 &nbsp;
configs/api_httpdns.yaml
<download-link :v-element="'rpc-code'" :v-file="'api_httpdns.yaml'">[下载]</download-link>
</td>
</tr>
@@ -20,45 +19,27 @@
<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>
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>
<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}} &nbsp; <a href=""
@click.prevent="showSSHPopup(nodeId)">[修改]</a></span>
<span v-else><span class="red">尚未设置</span> &nbsp; <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">
<h4>通过控制台标记安装</h4>
<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>
<span v-if="installStatus.isOk" class="green">安装成功</span>
<span v-else class="red">安装失败{{installStatus.error}}</span>
</div>
</div>
<div v-if="installStatus != null && installStatus.isFinished">
@@ -68,12 +49,12 @@
<button class="ui button small primary" type="button" @click.prevent="install()">开始安装</button>
</div>
<h4>方法2手动安装</h4>
<h4>配置文件</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">配置文件</td>
<td>
configs/api_httpdns.yaml &nbsp;
configs/api_httpdns.yaml
<download-link :v-element="'rpc-code'" :v-file="'api_httpdns.yaml'">[下载]</download-link>
</td>
</tr>
@@ -81,20 +62,18 @@
<td>配置内容</td>
<td>
<source-code-box id="rpc-code" type="text/yaml">rpc.endpoints: [ {{apiEndpoints}} ]
nodeId: "{{node.uniqueId}}"
secret: "{{node.secret}}"</source-code-box>
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>
<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>
<a href="" @click.prevent="updateNodeIsInstalled(true)">[修改为已安装]</a>
</div>

View File

@@ -7,7 +7,6 @@ Tea.context(function () {
this.install = function () {
isInstalling = true
this.$post("$")
.params({
nodeId: this.nodeId
@@ -17,8 +16,8 @@ Tea.context(function () {
this.updateNodeIsInstalled = function (isInstalled) {
let msg = isInstalled
? "html:确认要将当前节点修改为 <strong>已安装</strong> 状态?"
: "html:确认要将当前节点修改为 <strong>未安装</strong> 状态?"
? "html:确认要将当前节点修改为 <strong>已安装</strong> 状态"
: "html:确认要将当前节点修改为 <strong>未安装</strong> 状态"
teaweb.confirm(msg, function () {
this.$post("/httpdns/clusters/cluster/node/updateInstallStatus")
.params({
@@ -44,60 +43,16 @@ Tea.context(function () {
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
teaweb.warn("安装失败:" + (installStatus.error || "未知错误"))
}
})
.done(function () {
this.$delay(function () {
this.reloadStatus(nodeId)
that.reloadStatus(nodeId)
}, 1000)
})
}

View File

@@ -7,22 +7,22 @@
<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"/>
<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" value="" style="width:8em" id="day-to-picker"/>
<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">错误</option>
<option value="warning"></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="关键词"/>
<input type="text" name="keyword" style="width:14em" v-model="keyword" placeholder="类型/模块/详情"/>
</div>
<div class="ui field">
<button type="submit" class="ui button">查询</button>
@@ -36,16 +36,13 @@
<p class="comment" v-if="logs.length == 0">暂时还没有日志。</p>
<table class="ui table selectable" v-if="logs.length > 0">
<thead>
<tr>
<tbody>
<tr v-for="log in logs">
<td>
<node-log-row :v-log="log" :v-keyword="keyword"></node-log-row>
</td>
</tr>
</thead>
<tr v-for="log in logs">
<td>
<node-log-row :v-log="log" :v-keyword="keyword"></node-log-row>
</td>
</tr>
</tbody>
</table>
<div class="page" v-html="page"></div>
<div class="page" v-html="page"></div>

View File

@@ -4,8 +4,8 @@
<h3>修改节点</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<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>
@@ -13,66 +13,50 @@
<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">
<select class="ui dropdown" name="clusterId" style="width:16em" v-model="clusterId" disabled>
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
</select>
<p class="comment">当前版本暂不支持在此页面变更节点所属集群。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
<td>IP地址</td>
<td>
<node-ip-addresses-box :v-ip-addresses="ipAddresses" :v-node-id="node.id"></node-ip-addresses-box>
</td>
</tr>
<tr>
<td>启用当前节点</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" v-model="node.isOn" />
<label></label>
</div>
</td>
</tr>
<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" 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>
<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>
</form>

View File

@@ -1,35 +1,25 @@
Tea.context(function () {
this.clusterId = 0;
if (this.node.cluster != null && this.node.cluster.id > 0) {
this.clusterId = this.node.cluster.id;
}
if (typeof this.clusterId !== "number" || this.clusterId <= 0) {
this.clusterId = 0
}
if (this.clusterId <= 0 && 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.success = NotifySuccess("保存成功", "/httpdns/clusters/cluster/node?clusterId=" + this.clusterId + "&nodeId=" + this.node.id)
// 认证相关
this.grant = null
if (typeof this.sshHost !== "string") {
this.sshHost = ""
}
this.sshHost = ""
this.sshPort = ""
this.loginId = 0
if (this.node.login != null) {
this.loginId = this.node.login.id
let sshPort = parseInt(this.sshPort)
if (isNaN(sshPort) || sshPort <= 0) {
this.sshPort = 22
} else {
this.sshPort = sshPort
}
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
}
}
}
})
if (typeof this.loginId !== "number") {
this.loginId = 0
}
})

View File

@@ -16,6 +16,7 @@
<div class="right-box with-menu">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="clusterId" :value="cluster.id" />
<table class="ui table definition selectable" v-show="activeSection == 'basic'">
@@ -102,4 +103,4 @@
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

@@ -1,11 +1,16 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功");
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.activeSection = this.activeSection || "basic";
this.tlsAdvancedVisible = false;
this.activeSection = this.activeSection || "basic"
this.tlsAdvancedVisible = false
if (!this.settings) {
this.settings = {};
this.settings = {}
}
});
this.syncDefaultCluster = function () {
if (!this.settings.isOn) {
this.settings.isDefaultCluster = false
}
}
})

View File

@@ -1,23 +1,72 @@
{$layout}
{$layout}
{$template "menu"}
<div class="ui margin"></div>
<div class="ui message info">目前这是一个纯前端占位页面Mock后续将对接真实的后端 API。</div>
<form class="ui form">
<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">集群名称 *</td>
<td>
<input type="text" placeholder="如 gateway-cn-hz" />
<input type="text" name="name" maxlength="50" ref="focus" placeholder="如 gateway-cn-hz" />
<p class="comment">用于在系统内部标识该 HTTPDNS 集群。</p>
</td>
</tr>
<tr>
<td>集群服务域名 *</td>
<td>服务域名 *</td>
<td>
<input type="text" placeholder="如 gw-hz.httpdns.example.com" />
<input type="text" name="gatewayDomain" maxlength="255" placeholder="如 gw-hz.httpdns.example.com" />
<p class="comment">当前集群对外提供 HTTPDNS 服务的接入域名。</p>
</td>
</tr>
<tr>
<td>默认解析 TTL</td>
<td>
<div class="ui input right labeled">
<input type="text" name="cacheTtl" maxlength="5" value="30" style="width: 6em" />
<span class="ui label"></span>
</div>
<p class="comment">SDK 向 HTTPDNS 请求域名解析时,返回的默认缓存有效期 (TTL)。SDK 超时后将重新发起请求。</p>
</td>
</tr>
<tr>
<td>降级超时容忍度</td>
<td>
<div class="ui input right labeled">
<input type="text" name="fallbackTimeout" maxlength="5" value="300" style="width: 6em" />
<span class="ui label">毫秒</span>
</div>
<p class="comment">HTTPDNS 节点在回源查询其它 DNS 时的最大等待时间。超出此时间将触发服务降级逻辑(返回上一有效缓存或错误)。</p>
</td>
</tr>
<tr>
<td>节点安装根目录</td>
<td>
<input type="text" name="installDir" maxlength="255" value="/opt/edge-httpdns" />
<p class="comment">边缘节点安装 HTTPDNS 服务的默认所在目录,此目录将被用于下发配置。通常保持默认即可。</p>
</td>
</tr>
<tr>
<td>启用当前集群</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" checked />
<label></label>
</div>
<p class="comment">如果取消启用,整个集群的 HTTPDNS 解析服务将不被系统分配。</p>
</td>
</tr>
<tr>
<td>默认集群</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isDefault" value="1" />
<label>设置为默认部署集群</label>
</div>
<p class="comment">全局设置。如果应用未单独指定集群,将默认分配和部署到该集群中。</p>
</td>
</tr>
</table>
<button type="button" class="ui button primary">保存</button>
</form>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,17 @@
Tea.context(function () {
this.success = function (resp) {
let clusterId = 0
if (resp != null && resp.data != null && typeof resp.data.clusterId != "undefined") {
clusterId = resp.data.clusterId
}
if (clusterId > 0) {
teaweb.success("保存成功", function () {
window.location = "/httpdns/clusters/cluster?clusterId=" + clusterId
})
return
}
teaweb.success("保存成功", function () {
window.location = "/httpdns/clusters"
})
}
})

View File

@@ -7,6 +7,7 @@
</second-menu>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="clusterId" :value="clusterId" />
<table class="ui table definition selectable">
<tr>
@@ -16,12 +17,12 @@
</td>
</tr>
<tr>
<td>IP地址 *</td>
<td>安装目录</td>
<td>
<node-ip-addresses-box role="ns"></node-ip-addresses-box>
<p class="comment">用于访问节点和处理HTTPDNS解析请求等</p>
<input type="text" name="installDir" maxlength="100" :value="cluster.installDir || '/opt/edge-httpdns'" />
<p class="comment">默认使用集群配置目录</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</form>

View File

@@ -1,3 +1,3 @@
Tea.context(function () {
this.success = NotifySuccess("保存成功", "/httpdns/clusters/cluster?clusterId=" + this.clusterId);
});
this.success = NotifySuccess("保存成功", "/httpdns/clusters/cluster?clusterId=" + this.clusterId)
})

View File

@@ -8,4 +8,4 @@
<div class="buttons-box">
<button class="ui button red" type="button" @click.prevent="deleteCluster(cluster.id)">删除当前集群</button>
</div>
</div>

View File

@@ -1,31 +1,32 @@
{$layout "layout_popup"}
<h3>淇敼鑺傜偣"{{node.name}}"鐨凷SH鐧诲綍淇℃伅</h3>
<h3>修改节点"{{node.name}}"的SSH登录信息</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"/>
<csrf-token></csrf-token>
<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 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>
<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>SSH主机端口 *</td>
<td>
<input type="text" name="sshPort" maxlength="5" v-model="params.port" style="width:6em"/>
<p class="comment">姣斿22銆?/p>
<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>SSH登录认证 *</td>
<td>
<grant-selector :v-grant="grant" :v-node-cluster-id="clusterId"></grant-selector>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</form>

View File

@@ -1,4 +1,4 @@
Tea.context(function () {
Tea.context(function () {
if (typeof this.health == "undefined") {
this.health = {
keySyncRate: 1.0,
@@ -14,7 +14,12 @@ Tea.context(function () {
teaweb.popup("/httpdns/ech/rollbackMfaPopup?logId=" + logId, {
height: "26em",
width: "48em",
title: "全域安全受控降级告警双人MFA授权"
})
title: "全域安全受控降级告警双人MFA授权",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload();
});
}
});
};
});

View File

@@ -2,7 +2,7 @@
<h3>受控 ECH 回滚降级</h3>
<form method="post" class="ui form" data-tea-action="$">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="logId" :value="logId" />
<csrf-token></csrf-token>
<table class="ui table definition selectable">
@@ -24,4 +24,4 @@
</tr>
</table>
<submit-btn></submit-btn>
</form>
</form>

View File

@@ -1,4 +1,4 @@
{$layout}
{$layout}
{$template "menu"}
<style>
@@ -22,11 +22,11 @@
<div class="ui form" style="margin-bottom: 1.5em;">
<div class="ui fields inline">
<div class="field">
<label style="font-weight: 600;">目标应用</label>
<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 value="">[璇烽€夋嫨搴旂敤]</option>
<option v-for="app in apps" :value="app.appId">{{app.name}} ({{app.appId}})</option>
</select>
</div>
@@ -35,7 +35,7 @@
<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>
<p style="margin-top:1em; font-size:1.1em;">璇峰厛閫夋嫨搴旂敤锛岀劧鍚庢煡鐪嬮厤缃苟瀹屾垚 SDK 鎺ュ叆銆?/p>
</div>
<div v-show="selectedAppId.length > 0">
@@ -43,106 +43,104 @@
<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 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 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>
<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>
<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>搴旂敤鍚嶇О</td>
<td><strong>{{selectedApp.name}}</strong></td>
</tr>
<tr>
<td>集群服务地址</td>
<td>闆嗙兢鏈嶅姟鍦板潃</td>
<td>
<code>{{selectedApp.gatewayDomainDisplay}}</code>
<a href="" class="httpdns-mini-action" title="复制服务地址" @click.prevent="copyText(selectedApp.gatewayDomainDisplay, '服务地址')"><i class="copy outline icon"></i></a>
<p class="comment" v-if="selectedApp.gatewayDomains && selectedApp.gatewayDomains.length > 1">已启用主备:第一个为主集群,后续为备集群。</p>
<a href="" class="httpdns-mini-action" title="澶嶅埗鏈嶅姟鍦板潃" @click.prevent="copyText(selectedApp.gatewayDomainDisplay, '鏈嶅姟鍦板潃')"><i class="copy outline icon"></i></a>
<p class="comment" v-if="selectedApp.gatewayDomains && selectedApp.gatewayDomains.length > 1">宸插惎鐢ㄤ富澶囷細绗竴涓负涓婚泦缇わ紝鍚庣画涓哄闆嗙兢銆?/p>
</td>
</tr>
<tr>
<td>加签 Secret</td>
<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>
<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>请求验签</td>
<td>璇锋眰楠岀</td>
<td>
<span class="green" v-if="selectedApp.signEnabled">已开启</span>
<span class="grey" v-else>已关闭</span>
<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>
涓嬩竴姝?<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>
<h4 class="ui header">寮€鍙戞帴鍏?/h4>
<p class="grey">閫夋嫨瀵瑰簲骞冲彴 SDK 涓嬭浇骞舵煡闃呴泦鎴愭枃妗c€?/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>
閫傜敤浜?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>
<a class="ui button primary mini" href="https://github.com/aliyun/alibabacloud-httpdns-android-sdk" target="_blank" rel="noopener noreferrer"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button basic mini" href="https://github.com/aliyun/alibabacloud-httpdns-android-sdk/blob/master/README.md" target="_blank" rel="noopener noreferrer"><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>
閫傜敤浜?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>
<a class="ui button primary mini" href="https://github.com/aliyun/alibabacloud-httpdns-ios-sdk" target="_blank" rel="noopener noreferrer"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button basic mini" href="https://github.com/aliyun/alibabacloud-httpdns-ios-sdk/blob/master/README.md" target="_blank" rel="noopener noreferrer"><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>
璺ㄥ钩鍙?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>
<a class="ui button primary mini" href="https://pub.dev/packages/aliyun_httpdns" target="_blank" rel="noopener noreferrer"><i class="icon download"></i> 下载 SDK</a>
<a class="ui button basic mini" href="https://pub.dev/packages/aliyun_httpdns" target="_blank" rel="noopener noreferrer"><i class="icon book"></i> 集成帮助文档</a>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,30 +1,28 @@
{$layout}
{$template "menu"}
{$layout}
<div class="margin"></div>
{$template "/datepicker"}
<style>
.httpdns-runtime-level {
font-weight: 600;
}
.httpdns-runtime-level {
font-weight: 600;
}
.httpdns-runtime-message {
word-break: break-all;
}
.httpdns-runtime-time {
margin-right: 0.5em;
}
.httpdns-runtime-tag {
margin-right: 0.5em;
}
</style>
<form method="get" action="/httpdns/runtimeLogs" class="ui form small" 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:7.8em"
id="day-from-picker" />
@@ -35,10 +33,10 @@
<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>
<option value="error">错误</option>
<option value="warning">警告</option>
<option value="info">信息</option>
<option value="success">成功</option>
</select>
</div>
<div class="ui field">
@@ -47,8 +45,7 @@
<div class="ui field">
<button type="submit" class="ui button small">查询</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">
<div class="ui field" v-if="dayFrom.length > 0 || dayTo.length > 0 || keyword.length > 0 || level.length > 0">
<a href="/httpdns/runtimeLogs">[清除条件]</a>
</div>
</div>
@@ -58,31 +55,44 @@
<not-found-box v-if="runtimeLogs.length == 0">暂时还没有运行日志。</not-found-box>
<table class="ui table selectable celled" v-if="runtimeLogs.length > 0">
<table class="ui table celled" v-if="runtimeLogs.length > 0">
<thead>
<tr>
<th>时间</th>
<th>集群</th>
<th>节点</th>
<th>级别</th>
<th>类型</th>
<th>详情</th>
<th>次数</th>
<th>信息</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="httpdns-runtime-level"
:class="{red:log.level == 'error', orange:log.level == 'warning', green:log.level == 'success'}">{{log.level}}</span>
<td nowrap="">
<span v-if="log.clusterName">{{log.clusterName}}
<a :href="'/httpdns/clusters/cluster?clusterId=' + log.clusterId" target="_blank"
v-if="log.clusterId > 0"><i class="icon external small"></i></a>
</span>
<span v-else class="disabled">-</span>
</td>
<td nowrap="">
<span v-if="log.nodeName">{{log.nodeName}}
<a :href="'/httpdns/clusters/cluster/node?clusterId=' + log.clusterId + '&nodeId=' + log.nodeId"
target="_blank" v-if="log.nodeId > 0"><i class="icon external small"></i></a>
</span>
<span v-else class="disabled">-</span>
</td>
<td>
<div>
<span
:class="{red:log.level == 'error', orange:log.level == 'warning', green:log.level == 'success' || log.level == 'info'}"
class="httpdns-runtime-message">
<span class="httpdns-runtime-time">[{{log.createdTime}}]</span>
<span class="httpdns-runtime-tag" v-if="log.tag && log.tag.length > 0">[{{log.tag}}]</span>
<span class="httpdns-runtime-level">{{log.description}}</span>
</span>
<span v-if="log.count > 1" class="ui label tiny basic"
:class="{red:log.level == 'error', orange:log.level == 'warning', green:log.level == 'success' || log.level == 'info'}"
style="margin-left: 0.5em">共{{log.count}}条</span>
</div>
</td>
<td><code>{{log.tag}}</code></td>
<td>{{log.description}}</td>
<td>{{log.count}}</td>
</tr>
</tbody>
</table>
</table>

View File

@@ -1,4 +1,7 @@
Tea.context(function () {
this.clusterId = "";
this.nodeId = "";
this.$delay(function () {
teaweb.datepicker("day-from-picker");
teaweb.datepicker("day-to-picker");

View File

@@ -20,7 +20,7 @@
<label>所属集群 *</label>
<select class="ui dropdown" name="clusterId" v-model="request.clusterId">
<option value="">[请选择所属集群]</option>
<option v-for="cluster in clusters" :value="String(cluster.id)">{{cluster.name}}</option>
<option v-for="cluster in currentClusters" :value="String(cluster.id)">{{cluster.name}}</option>
</select>
</div>
<div class="field">

View File

@@ -22,6 +22,7 @@ Tea.context(function () {
this.isRequesting = false
this.currentDomains = []
this.currentClusters = []
if (typeof this.apps === "undefined") {
this.apps = []
@@ -41,6 +42,7 @@ Tea.context(function () {
if (selectedApp == null) {
this.currentDomains = []
this.currentClusters = []
this.request.domain = ""
this.request.clusterId = ""
return
@@ -56,8 +58,28 @@ Tea.context(function () {
this.request.domain = ""
}
if (typeof selectedApp.clusterId !== "undefined" && selectedApp.clusterId !== null) {
this.request.clusterId = String(selectedApp.clusterId)
let primaryClusterId = (typeof selectedApp.primaryClusterId !== "undefined" && selectedApp.primaryClusterId !== null) ? Number(selectedApp.primaryClusterId) : 0
let backupClusterId = (typeof selectedApp.backupClusterId !== "undefined" && selectedApp.backupClusterId !== null) ? Number(selectedApp.backupClusterId) : 0
let allowed = []
for (let i = 0; i < this.clusters.length; i++) {
let cluster = this.clusters[i]
let clusterId = Number(cluster.id)
if (clusterId <= 0) {
continue
}
if (clusterId === primaryClusterId || clusterId === backupClusterId) {
allowed.push(cluster)
}
}
this.currentClusters = allowed
if (allowed.length > 0) {
if (!allowed.some((c) => String(c.id) === String(this.request.clusterId))) {
this.request.clusterId = String(allowed[0].id)
}
} else {
this.request.clusterId = ""
}
}
@@ -94,15 +116,15 @@ Tea.context(function () {
this.sendTestRequest = function () {
if (this.request.appId.length === 0) {
teaweb.warn("Please select target app")
teaweb.warn("请选择目标应用")
return
}
if (this.request.clusterId.length === 0) {
teaweb.warn("Please select cluster")
teaweb.warn("当前应用未绑定可用集群,请先在应用设置中配置主集群/备集群")
return
}
if (this.request.domain.length === 0) {
teaweb.warn("Please select domain")
teaweb.warn("请选择要解析的域名")
return
}
@@ -126,6 +148,7 @@ Tea.context(function () {
this.resetForm = function () {
this.request = this.newRequest()
this.currentDomains = []
this.currentClusters = []
this.response = {
hasResult: false,
code: -1,