Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
<second-menu>
<menu-item :href="'/clusters/cluster?clusterId=' + clusterId">{{currentClusterName}}</menu-item>
<raquo-item></raquo-item>
<menu-item :href="'/clusters/cluster/settings?clusterId=' + clusterId">集群设置</menu-item>
<raquo-item></raquo-item>
<a class="item active" v-if="leftMenuActiveItem != null" :href="leftMenuActiveItem.url">"{{leftMenuActiveItem.name}}"设置</a>
</second-menu>

View File

@@ -0,0 +1,20 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">选择缓存策略 *</td>
<td>
<http-cache-policy-selector :v-cache-policy="cachePolicy"></http-cache-policy-selector>
<p class="comment">部署在当前集群上的所有网站都会自动使用此策略,只是服务可以自定义是否开启。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,109 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">启用CC防护功能</td>
<td>
<checkbox name="isOn" v-model="httpCCPolicy.isOn"></checkbox>
<p class="comment">选中后表示当前集群下的网站可以使用CC防护功能。</p>
</td>
</tr>
<tbody v-show="httpCCPolicy.isOn">
<tr v-for="(defaultThreshold, index) in defaultThresholds">
<td>检测阈值{{index+1}}</td>
<td>
<table class="ui table">
<tr>
<td class="title">计算周期</td>
<td>
<div class="ui input right labeled">
<input type="text" name="thresholdPeriodSeconds" style="width: 5em" maxlength="4" v-model="thresholds[index].periodSeconds"/>
<span class="ui label"></span>
</div>
<p class="comment">0表示默认默认{{defaultThreshold.periodSeconds}}秒。</p>
</td>
</tr>
<tr>
<td>最大请求数</td>
<td>
<input type="text" name="thresholdMaxRequests" style="width: 6em" maxlength="5" v-model="thresholds[index].maxRequests"/>
<p class="comment">0表示默认默认{{defaultThreshold.maxRequests}}。</p>
</td>
</tr>
<tr>
<td>拦截时间</td>
<td>
<div class="ui input right labeled">
<input type="text" name="thresholdBlockSeconds" style="width: 5em" maxlength="4" v-model="thresholds[index].blockSeconds"/>
<span class="ui label"></span>
</div>
<p class="comment">0表示默认默认{{defaultThreshold.blockSeconds}}秒。</p>
</td>
</tr>
</table>
<p class="comment"><pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>单IP最大并发连接数</td>
<td>
<input type="text" name="maxConnectionsPerIP" v-model="httpCCPolicy.maxConnectionsPerIP" maxlength="6" style="width: 6em"/>
<p class="comment">如果为0表示使用默认默认为{{defaultHTTPCCPolicyMaxConnectionsPerIP}}。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>跳转检测路径</td>
<td>
<input type="text" name="redirectsCheckingValidatePath" v-model="httpCCPolicy.redirectsChecking.validatePath"/>
<p class="comment">如果不填表示使用默认,默认为{{defaultHTTPCCPolicyRedirectsCheckingValidatePath}}必须是一个特殊的URL以避免用户访问异常。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>跳转检测周期</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="4" style="width: 5em" name="redirectsCheckingDurationSeconds" v-model="httpCCPolicy.redirectsChecking.durationSeconds"/>
<span class="ui label"></span>
</div>
<p class="comment">如果为0表示使用默认默认为{{defaultHTTPCCPolicyRedirectsCheckingDurationSeconds}}。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>跳转检测最多次数</td>
<td>
<input type="text" maxlength="4" style="width: 5em" name="redirectsCheckingMaxRedirects" v-model="httpCCPolicy.redirectsChecking.maxRedirects"/>
<p class="comment">如果为0表示使用默认默认为{{defaultHTTPCCPolicyRedirectsCheckingMaxRedirects}}。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>跳转检测拦截时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="6" style="width: 6em" name="redirectsCheckingBlockSeconds" v-model="httpCCPolicy.redirectsChecking.blockSeconds"/>
<span class="ui label"></span>
</div>
<p class="comment">如果为0表示使用默认默认为{{defaultHTTPCCPolicyRedirectsCheckingBlockSeconds}}。<pro-warning-label></pro-warning-label></p>
</td>
</tr>
<tr>
<td>防火墙拦截范围</td>
<td>
<select name="firewallScope" v-model="httpCCPolicy.firewall.scope" class="ui dropdown auto-width">
<option value="global">所有网站</option>
<option value="service">当前网站</option>
</select>
<p class="comment" v-if="httpCCPolicy.firewall.scope == 'global'">“所有网站”表示IP被拦截时将被阻止访问所有网站此时系统可以自动尝试利用本地防火墙提升拦截效率。</p>
<p class="comment" v-if="httpCCPolicy.firewall.scope == 'service'">“当前网站”表示IP被拦截时只会阻止访问当前网站选择此选项会严重影响拦截效率建议只有特殊情况下才使用。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,19 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item :href="'.?clusterId=' + clusterId" class="active">设置</menu-item>
<menu-item :href="'.status?clusterId=' + clusterId" >状态</menu-item>
</first-menu>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="clusterId" :value="clusterId"/>
<node-ddos-protection-config-box :v-ddos-protection-config="config" :v-default-configs="defaultConfigs"></node-ddos-protection-config-box>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,44 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item :href="'.?clusterId=' + clusterId">设置</menu-item>
<menu-item :href="'.status?clusterId=' + clusterId" class="active">状态</menu-item>
</first-menu>
<loading-message v-if="isLoading">检查中,请耐心等待完成...</loading-message>
<p class="comment" v-if="!isLoading && results.length == 0">当前集群下暂时还没有节点。</p>
<table class="ui table selectable" v-if="results.length > 0">
<thead>
<tr>
<th class="three wide">节点名称</th>
<th>检查结果</th>
<th class="two op">独立设置</th>
</tr>
</thead>
<tbody v-for="result in results">
<tr>
<td>
<a :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + result.nodeId">{{result.nodeName}}</a>
<div v-if="result.isPrior">
<a :href="'/clusters/cluster/node/settings/ddos-protection?clusterId=' + clusterId + '&nodeId=' + result.nodeId"><grey-label>定制</grey-label></a>
</div>
</td>
<td>
<span class="green" v-if="result.isOk">{{result.message}}</span>
<span class="red" v-if="!result.isOk">{{result.message}}</span>
</td>
<td>
<a :href="'/clusters/cluster/node/settings/ddos-protection?clusterId=' + clusterId + '&nodeId=' + result.nodeId">设置</a>
</td>
</tr>
</tbody>
</table>
<div class="margin"></div>
<a href="" @click.prevent="reload">[刷新]</a>
</div>

View File

@@ -0,0 +1,20 @@
Tea.context(function () {
this.isLoading = true
this.results = []
this.$delay(function () {
this.reload()
})
this.reload = function () {
this.isLoading = true
this.$post("$")
.params({ clusterId: this.clusterId })
.success(function (resp) {
this.results = resp.data.results
})
.done(function () {
this.isLoading = false
})
}
})

View File

@@ -0,0 +1,4 @@
<first-menu>
<menu-item :href="'.?clusterId=' + clusterId" code="index">DNS设置</menu-item>
<menu-item :href="'.records?clusterId=' + clusterId" code="records">解析记录</menu-item>
</first-menu>

View File

@@ -0,0 +1,101 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="clusterId" :value="clusterId"/>
<csrf-token></csrf-token>
<table class="ui table selectable definition">
<tr>
<td>选择主域名 *</td>
<td>
<div v-if="hasDomains">
<dns-domain-selector :v-domain-id="domainId" :v-domain-name="domainName" :v-provider-name="domainProvider.name" @change="changeDomain"></dns-domain-selector>
<p class="comment">用于生成集群节点和网站的CNAME和DNS解析记录<span v-if="domainId > 0">,修改后将自动删除旧域名中的相关记录</span></p>
</div>
<div v-else>
没有可用的域名,请在 <a href="/dns/providers" target="_blank">[域名解析]</a> 中添加。
</div>
</td>
</tr>
<tr v-if="oldDomain.id > 0 && domain.id == 0">
<td>不使用主域名</td>
<td>
<checkbox name="confirmResetDomain"></checkbox>
<p class="comment">选中后,表示确认当前集群不使用主域名;否则必须选择一个主域名。</p>
</td>
</tr>
<tr>
<td class="title">DNS子域名 *</td>
<td>
<div class="ui input right labeled">
<input type="text" name="dnsName" maxlength="64" style="width:12em" v-model="dnsName"/>
<span class="ui label" v-if="domain != null && domain.name != null && domain.name.length > 0">.{{domain.name}}</span>
<span class="ui label" v-else>.主域名</span>
</div>
<p class="comment">当前集群的子域名,和主域名一起组成集群节点的子域名,修改后将自动删除旧域名中的相关记录。<a href="" @click.prevent="generateRandName()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>自动设置CNAME记录<optional-label></optional-label></td>
<td>
<values-box :values="cnameRecords" name="cnameRecords" placeholder="记录名" ref="cnameRecords"></values-box>
<p class="comment">除集群已创建的网站之外自动解析到集群的CNAME记录比如<code-label @click.prevent="addCnameRecord('www')">www</code-label></p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>允许通过CNAME访问网站</td>
<td>
<checkbox name="cnameAsDomain" v-model="cnameAsDomain"></checkbox>
<p class="comment">选中后表示允许使用CNAME直接访问网站如果取消选中则表示CNAME只作为DNS解析记录使用。</p>
</td>
</tr>
<tr v-show="teaIsPlus">
<td>包含Ln节点</td>
<td>
<checkbox name="includingLnNodes" v-model="includingLnNodes"></checkbox>
<p class="comment">选中后表示域名解析中包含L2及以上级别节点也就意味着用户请求可能会被分配到这些节点。</p>
</td>
</tr>
<tr>
<td>解析记录TTL</td>
<td>
<div class="ui input right labeled">
<input type="text" name="ttl" maxlength="6" style="width: 6em" v-model="ttl"/>
<span class="ui label"></span>
</div>
<p class="comment">每个DNS服务商或者账号的TTL限制各有不同请注意取值范围修改后只对新的解析记录生效。0表示使用默认。</p>
</td>
</tr>
<tr>
<td>同步节点DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="nodesAutoSync" value="1" v-model="nodesAutoSync"/>
<label></label>
</div>
</td>
</tr>
<tr>
<td>同步网站DNS状态</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="serversAutoSync" value="1" v-model="serversAutoSync"/>
<label></label>
</div>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,21 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.domain = {id: this.domainId, name: this.domainName}
this.oldDomain = {id: this.domainId, name: this.domainName}
this.changeDomain = function (domain) {
this.domain.id = domain.id
this.domain.name = domain.name
}
this.addCnameRecord = function (name) {
this.$refs.cnameRecords.addValue(name)
}
this.generateRandName = function () {
this.$post(".randomName")
.success(function (resp) {
this.dnsName = resp.data.name
})
}
})

View File

@@ -0,0 +1,203 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<!-- 基本信息 -->
<table class="ui table definition selectable">
<tr>
<td class="title">DNS子域名</td>
<td>
<span v-if="dnsInfo.domainName.length > 0"><var>{{dnsInfo.dnsName}}</var>.{{dnsInfo.domainName}}</span>
<span v-else class="disabled">没有设置</span>
</td>
</tr>
<tr>
<td>DNS服务商</td>
<td>
<div v-if="dnsInfo.providerName.length > 0">
<link-icon :href="'/dns/providers/provider?providerId=' + dnsInfo.providerId">{{dnsInfo.providerTypeName}} - {{dnsInfo.providerName}}</link-icon>
</div>
<span v-else-if="dnsInfo.domainName.length == 0" class="disabled">请先设置域名</span>
<span v-else class="disabled">没有设置</span>
</td>
</tr>
<tr>
<td>自动设置CNAME记录</td>
<td>
<span v-if="dnsInfo.cnameRecords.length == 0" class="disabled">暂时还没有设置。</span>
<div v-else>
<span v-for="record in dnsInfo.cnameRecords" class="ui label basic small">{{record}}</span>
</div>
</td>
</tr>
<tr v-if="dnsInfo.domainName.length > 0">
<td>操作</td>
<td>
<div v-if="!isSyncing">
<link-red v-if="dnsHasChanges" @click.prevent="syncCluster(cluster.id)">检测到解析记录有变化,需要同步</link-red>
<a href="" @click.prevent="syncCluster(cluster.id)" v-else>DNS服务商同步</a>
</div>
<span v-else>DNS服务商同步中...</span>
</td>
</tr>
</table>
<!-- 当前任务 -->
<div v-if="tasks.length > 0">
<h3>正在执行的任务</h3>
<table class="ui table selectable celled">
<thead>
<tr>
<th>对象</th>
<th>任务</th>
<th>状态</th>
<th>触发时间</th>
<th></th>
</tr>
</thead>
<tr v-for="task in tasks">
<td>
<span v-if="(task.type == 'clusterChange' || task.type == 'clusterNodesChange') && task.cluster != null">{{task.cluster.name}}
<link-icon :href="'/dns/clusters/cluster?clusterId=' + task.cluster.id" target="_top"></link-icon>
</span>
<span v-if="task.type == 'nodeChange'">{{task.node.name}}</span>
<span v-if="task.type == 'serverChange'">{{task.server.name}}</span>
<span v-if="task.type == 'domainChange'">{{task.domain.name}}</span>
</td>
<td>
<span v-if="task.type == 'clusterChange' || task.type == 'clusterNodesChange'">集群</span>
<span v-if="task.type == 'nodeChange'">节点</span>
<span v-if="task.type == 'serverChange'">网站</span>
<span v-if="task.type == 'domainChange'">域名</span>
</td>
<td style="word-break: break-word; width: 26em">
<span v-if="task.isDone" class="red">{{task.error}}</span>
<span v-else>正在同步...</span>
</td>
<td>{{task.updatedTime}}</td>
<td>
<a href="" title="删除" class="remove-btn" @click.prevent="deleteTask(task.id)"><i class="icon remove small grey"></i></a>
</td>
</tr>
</table>
</div>
<!-- 问题合集 -->
<div v-if="issues.length > 0">
<h3>需要修复的问题</h3>
<table class="ui table selectable celled" v-if="issues.length > 0">
<thead>
<tr>
<th style="width: 50%">问题对象</th>
<th>问题描述</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="issue in issues">
<td>
<div v-if="issue.type == 'cluster'">
集群 "{{issue.target}}" <link-icon :href="'/clusters/cluster?clusterId=' + issue.targetId"></link-icon>
</div>
<div v-if="issue.type == 'node'">
集群 "{{issue.params.clusterName}}" 节点 "{{issue.target}}" <link-icon :href="'/clusters/cluster/node?clusterId=' + issue.params.clusterId + '&nodeId=' + issue.targetId"></link-icon>
</div>
</td>
<td>
<span>{{issue.description}}</span>
</td>
<td>
<div v-if="issue.type == 'cluster'">
<link-red @click.prevent="updateCluster(issue.targetId)">修复</link-red>
</div>
<div v-if="issue.type == 'node'">
<link-red @click.prevent="updateNode(issue.params.clusterId, issue.targetId)">修复</link-red>
</div>
</td>
</tr>
</table>
<div class="margin"></div>
</div>
<p class="comment">下面的DNS解析记录也可以手工在DNS服务商提供的管理平台添加。</p>
<!-- 节点DNS解析记录 -->
<h3>节点DNS解析记录 <span>&nbsp; ({{nodes.length}}个)</span></h3>
<p class="comment" v-if="nodes.length == 0">暂时没有需要设置的DNS记录。</p>
<table class="ui table selectable celled" v-if="nodes.length > 0">
<thead>
<tr>
<th>节点</th>
<th>子域名</th>
<th>记录类型</th>
<th>记录值</th>
<th>线路</th>
<th>状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="node in nodes">
<td><link-icon :href="'/clusters/cluster/node?clusterId=' + node.clusterId + '&nodeId=' + node.id">{{node.name}}</link-icon></td>
<td>
<span v-if="dnsInfo.dnsName.length > 0">{{dnsInfo.dnsName}}</span>
<link-red v-else @click.prevent="updateCluster(cluster.id)">没有设置</link-red>
</td>
<td>
<span v-if="node.ipAddr.indexOf(':') > -1">AAAA</span>
<span v-else>A</span>
</td>
<td>
<span v-if="node.ipAddr.length > 0">{{node.ipAddr}}</span>
<link-red title="点击设置" v-else @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">没有设置</link-red>
</td>
<td>
<span v-if="node.route.code.length > 0">{{node.route.name}}</span>
<link-red v-else title="点击设置" @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">没有设置</link-red>
</td>
<td>
<span v-if="node.isBackup" class="red">备用节点</span>
<span v-else-if="node.isOffline" class="red">已下线</span>
<div v-else="">
<span v-if="node.isInstalled">
<span class="green" v-if="node.isResolved">已解析</span>
<span v-else class="red">未解析</span>
</span>
<link-red :href="'/clusters/cluster/node/install?clusterId=' + cluster.id + '&nodeId=' + node.id" v-if="!node.isInstalled" title="节点未安装"><span class="red">未安装</span></link-red>
</div>
</td>
<td>
<link-popup @click.prevent="updateNode(node.clusterId, node.id, node.ipAddrId)">修改</link-popup>
</td>
</tr>
</table>
<!-- 网站解析记录 -->
<h3>网站解析记录 <span>&nbsp; ({{servers.length}}个)</span></h3>
<p class="comment" v-if="servers.length == 0">暂时没有需要设置的DNS记录。</p>
<table class="ui table selectable celled" v-if="servers.length > 0">
<thead>
<tr>
<th>网站</th>
<th>子域名</th>
<th>记录类型</th>
<th>记录值</th>
<th>状态</th>
</tr>
</thead>
<tr v-for="server in servers">
<td><link-icon :href="'/servers/server?serverId=' + server.id">{{server.name}}</link-icon> </td>
<td>{{server.dnsName}}</td>
<td>CNAME</td>
<td>
<span v-if="dnsInfo.domainName.length > 0"><var>{{dnsInfo.dnsName}}</var>.{{dnsInfo.domainName}}.</span>
<link-red title="点击设置" v-else @click.prevent="updateCluster(cluster.id)">没有设置</link-red>
</td>
<td>
<span class="green" v-if="server.isResolved">已解析</span>
<span v-else class="red">未解析</span>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,56 @@
Tea.context(function () {
this.updateCluster = function (clusterId) {
teaweb.popup("/dns/updateClusterPopup?clusterId=" + clusterId, {
height: "25em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.updateNode = function (clusterId, nodeId, ipAddrId) {
teaweb.popup("/dns/issues/updateNodePopup?clusterId=" + clusterId + "&nodeId=" + nodeId + "&ipAddrId=" + (ipAddrId ? ipAddrId : 0), {
width: "46em",
height: "26em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.isSyncing = false
this.syncCluster = function (clusterId) {
let that = this
teaweb.confirm("确定要执行数据同步吗?", function () {
that.isSyncing = true
that.$post("/dns/clusters/sync")
.params({clusterId: clusterId})
.done(function () {
that.isSyncing = false
that.dnsHasChanges = false
})
.success(function () {
teaweb.success("同步成功", function () {
teaweb.reload()
})
})
})
}
this.deleteTask = function (taskId) {
let that = this
teaweb.confirm("确定要删除这个任务吗?", function () {
that.$post("/dns/tasks/delete")
.params({
taskId: taskId
})
.success(function () {
teaweb.reload()
})
})
}
})

View File

@@ -0,0 +1,5 @@
.white-span {
background: white;
border: 1px;
}
/*# sourceMappingURL=createPopup.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["createPopup.less"],"names":[],"mappings":"AAAA;EACC,iBAAA;EACA,WAAA","file":"createPopup.css"}

View File

@@ -0,0 +1,130 @@
{$layout "layout_popup"}
<h3>添加动作</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="clusterId" :value="clusterId"/>
<csrf-token></csrf-token>
<table class="ui table selectable definition">
<tr>
<td class="title">名称 *</td>
<td>
<input type="text" name="name" maxlength="50" ref="focus"/>
</td>
</tr>
<tr>
<td>级别</td>
<td>
<firewall-event-level-options :v-value="'critical'"></firewall-event-level-options>
</td>
</tr>
<tr>
<td>类型</td>
<td>
<select class="ui dropdown auto-width" name="type" v-model="type">
<option value="">[请选择]</option>
<option v-for="type in actionTypes" :value="type.code">{{type.name}}</option>
</select>
<p class="comment">{{typeDescription}}</p>
</td>
</tr>
<!-- IPSet -->
<tbody v-if="type == 'ipset'">
<tr>
<td>IPv4白名单名称 *</td>
<td>
<input type="text" name="ipsetWhiteName" value="edge_white_list" maxlength="64"/>
<p class="comment"><span class="ui label basic grey tiny">白名单</span> 用来存放<span class="red">IPv4地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv6白名单名称 *</td>
<td>
<input type="text" name="ipsetWhiteNameIPv6" value="edge_white_list_ipv6" maxlength="64"/>
<p class="comment"><span class="ui label basic grey tiny">白名单</span> 用来存放<span class="red">IPv6地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv4黑名单名称 *</td>
<td>
<input type="text" name="ipsetBlackName" value="edge_black_list" maxlength="64"/>
<p class="comment"><span class="ui label black tiny">黑名单</span> 用来存放<span class="red">IPv4地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv6 黑名单名称 *</td>
<td>
<input type="text" name="ipsetBlackNameIPv6" value="edge_black_list_ipv6" maxlength="64"/>
<p class="comment"><span class="ui label black tiny">黑名单</span> 用来存放<span class="red">IPv6地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>创建IPTables规则</td>
<td>
<checkbox name="ipsetAutoAddToIPTables" :value="true"></checkbox>
<p class="comment">是否尝试自动创建包含有此IPSet的IPTables规则。</p>
</td>
</tr>
<tr>
<td>创建Firewalld规则</td>
<td>
<checkbox name="ipsetAutoAddToFirewalld" :value="true"></checkbox>
<p class="comment">是否尝试自动创建包含有此IPSet的Firewalld规则。</p>
</td>
</tr>
</tbody>
<!-- Firewalld -->
<tbody v-if="type == 'firewalld'">
</tbody>
<!-- IPTables -->
<tbody v-if="type == 'iptables'">
</tbody>
<!-- 脚本 -->
<tbody v-if="type == 'script'">
<tr>
<td>脚本路径 *</td>
<td>
<input type="text" name="scriptPath" maxlength="200"/>
<p class="comment">可执行脚本文件的完整路径。</p>
</td>
</tr>
</tbody>
<!-- HTTP API -->
<tbody v-if="type == 'httpAPI'">
<tr>
<td>API URL *</td>
<td>
<input type="text" name="httpAPIURL" maxlength="200" placeholder="http|https://..."/>
<p class="comment">完整的API地址。</p>
</td>
</tr>
</tbody>
<!-- HTML -->
<tbody v-if="type == 'html'">
<tr>
<td>HTML内容 *</td>
<td>
<textarea name="htmlContent" maxlength="128000" placeholder="完整的HTML内容">&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;zh&#34;&gt;
&lt;head&gt;
&lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 这里写提示文字 --&gt;
&lt;/body&gt;
&lt;/html&gt;</textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,30 @@
Tea.context(function () {
this.$delay(function () {
let that = this
// 类型
this.$watch("type", function () {
that.changeType()
})
this.changeType()
})
/**
* 类型
*/
this.type = ""
this.typeDescription = ""
this.changeType = function () {
let that = this
let t = this.actionTypes.$find(function (k, v) {
return v.code == that.type
})
if (t != null) {
this.typeDescription = t.description
} else {
this.typeDescription = ""
}
}
})

View File

@@ -0,0 +1,37 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item @click.prevent="createAction">添加动作</menu-item>
<span class="item disabled">|</span>
<span class="item"><tip-icon content="可以设置WAF中的规则集和IP名单中的IP级别从而在匹配时触发对应的动作。"></tip-icon></span>
</first-menu>
<div class="margin"></div>
<div v-if="!hasActions">
<p class="comment">暂时还没有自定义动作。</p>
</div>
<div v-for="level in levels" v-if="level.actions.length > 0">
<h4 style="margin-bottom: 0">{{level.name}}级别</h4>
<p class="comment" v-if="level.actions.length == 0">暂时还没有定义动作。</p>
<table class="ui table selectable" v-if="level.actions.length > 0">
<thead>
<tr>
<th>名称</th>
<th class="four wide">类型</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="action in level.actions">
<td>{{action.name}}</td>
<td>{{action.typeName}}</td>
<td>
<a href="" @click.prevent="updateAction(action.id)">修改</a> &nbsp; <a href="" @click.prevent="deleteAction(action.id)">删除</a>
</td>
</tr>
</table>
<div class="ui divider"></div>
</div>
</div>

View File

@@ -0,0 +1,36 @@
Tea.context(function () {
this.createAction = function () {
teaweb.popup(Tea.url(".createPopup", {clusterId: this.clusterId}), {
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.updateAction = function (actionId) {
teaweb.popup(Tea.url(".updatePopup", {actionId: actionId}), {
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.deleteAction = function (actionId) {
let that = this
teaweb.confirm("确定要删除此动作吗?", function () {
that.$post(".delete")
.params({
actionId: actionId
})
.success(function () {
teaweb.success("删除成功", function () {
teaweb.reload()
})
})
})
}
})

View File

@@ -0,0 +1,121 @@
{$layout "layout_popup"}
<h3>修改动作</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="actionId" :value="action.id"/>
<csrf-token></csrf-token>
<table class="ui table selectable definition">
<tr>
<td class="title">名称 *</td>
<td>
<input type="text" name="name" maxlength="50" ref="focus" v-model="action.name"/>
</td>
</tr>
<tr>
<td>级别</td>
<td>
<firewall-event-level-options :v-value="eventLevel"></firewall-event-level-options>
</td>
</tr>
<tr>
<td>类型</td>
<td>
<select class="ui dropdown auto-width" name="type" v-model="type">
<option value="">[请选择]</option>
<option v-for="type in actionTypes" :value="type.code">{{type.name}}</option>
</select>
<p class="comment">{{typeDescription}}</p>
</td>
</tr>
<!-- IPSet -->
<tbody v-if="type == 'ipset'">
<tr>
<td>IPv4白名单名称 *</td>
<td>
<input type="text" name="ipsetWhiteName" value="edge_white_list" maxlength="64" v-model="action.params.whiteName"/>
<p class="comment"><span class="ui label basic grey tiny">白名单</span> 用来存放<span class="red">IPv4地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv6白名单名称 *</td>
<td>
<input type="text" name="ipsetWhiteNameIPv6" value="edge_white_list_ipv6" maxlength="64" v-model="action.params.whiteNameIPv6"/>
<p class="comment"><span class="ui label basic grey tiny">白名单</span> 用来存放<span class="red">IPv6地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv4黑名单名称 *</td>
<td>
<input type="text" name="ipsetBlackName" value="edge_black_list" maxlength="64" v-model="action.params.blackName"/>
<p class="comment"><span class="ui label black tiny">黑名单</span> 用来存放<span class="red">IPv4地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>IPv6 黑名单名称 *</td>
<td>
<input type="text" name="ipsetBlackNameIPv6" value="edge_black_list_ipv6" maxlength="64" v-model="action.params.blackNameIPv6"/>
<p class="comment"><span class="ui label black tiny">黑名单</span> 用来存放<span class="red">IPv6地址</span>。只能是英文、数字、下划线的组合。</p>
</td>
</tr>
<tr>
<td>创建IPTables规则</td>
<td>
<checkbox name="ipsetAutoAddToIPTables" v-model="action.params.autoAddToIPTables"></checkbox>
<p class="comment">是否尝试自动创建包含有此IPSet的IPTables规则。</p>
</td>
</tr>
<tr>
<td>创建Firewalld规则</td>
<td>
<checkbox name="ipsetAutoAddToFirewalld" v-model="action.params.autoAddToFirewalld"></checkbox>
<p class="comment">是否尝试自动创建包含有此IPSet的Firewalld规则。</p>
</td>
</tr>
</tbody>
<!-- Firewalld -->
<tbody v-if="type == 'firewalld'">
</tbody>
<!-- IPTables -->
<tbody v-if="type == 'iptables'">
</tbody>
<!-- 脚本 -->
<tbody v-if="type == 'script'">
<tr>
<td>脚本路径 *</td>
<td>
<input type="text" name="scriptPath" maxlength="200" v-model="action.params.path"/>
<p class="comment">可执行脚本文件的完整路径。</p>
</td>
</tr>
</tbody>
<!-- HTTP API -->
<tbody v-if="type == 'httpAPI'">
<tr>
<td>API URL *</td>
<td>
<input type="text" name="httpAPIURL" maxlength="200" placeholder="http|https://..." v-model="action.params.url"/>
<p class="comment">完整的API地址。</p>
</td>
</tr>
</tbody>
<!-- HTML -->
<tbody v-if="type == 'html'">
<tr>
<td>HTML内容 *</td>
<td>
<textarea name="htmlContent" maxlength="128000" placeholder="完整的HTML内容" v-model="action.params.content"></textarea>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,35 @@
Tea.context(function () {
this.$delay(function () {
let that = this
// 类型
this.$watch("type", function () {
that.changeType()
})
this.changeType()
})
/**
* 级别
*/
this.eventLevel = this.action.eventLevel
/**
* 类型
*/
this.type = this.action.type
this.typeDescription = ""
this.changeType = function () {
let that = this
let t = this.actionTypes.$find(function (k, v) {
return v.code == that.type
})
if (t != null) {
this.typeDescription = t.description
} else {
this.typeDescription = ""
}
}
})

View File

@@ -0,0 +1,313 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<second-menu>
<a v-for="item in titleMenus" :class="{active: item.id == currentItem}" :href="'#' + item.id" class="item" @click="selectItem(item)">{{item.name}}</a>
</second-menu>
<form class="ui form" data-tea-action="$" data-tea-success="success" id="global-config-form">
<csrf-token></csrf-token>
<input type="hidden" name="clusterId" :value="clusterId"/>
<div v-show="currentItem == 'title-server-name-bind'">
<h4 id="title-server-name-bind">域名绑定</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">禁止未绑定域名访问</td>
<td>
<checkbox name="httpAllMatchDomainStrictly" v-model="config.httpAll.matchDomainStrictly"></checkbox>
<p class="comment">选中后表示禁止未在网站绑定的域名和IP访问。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly">
<td class="color-border">处理未绑定域名方式</td>
<td>
<radio name="httpAllDomainMismatchActionCode" :v-value="'page'" v-model="httpAllDomainMismatchActionCode">显示提示页面</radio> &nbsp;
&nbsp; <radio name="httpAllDomainMismatchActionCode" :v-value="'redirect'" v-model="httpAllDomainMismatchActionCode">跳转到网址</radio> &nbsp;
<radio name="httpAllDomainMismatchActionCode" :v-value="'close'" v-model="httpAllDomainMismatchActionCode">关闭连接</radio>
<p class="comment" v-if="httpAllDomainMismatchActionCode == 'page'">显示提示内容。</p>
<p class="comment" v-if="httpAllDomainMismatchActionCode == 'redirect'">跳转到一个特定URL。</p>
<p class="comment" v-if="httpAllDomainMismatchActionCode == 'close'">直接关闭网络连接,不提示任何内容。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly && httpAllDomainMismatchActionCode == 'page'">
<td class="color-border">提示页面状态码</td>
<td>
<input type="text" name="httpAllDomainMismatchActionStatusCode" v-model="httpAllDomainMismatchActionStatusCode" style="width: 4em" maxlength="3"/>
<p class="comment">访问未绑定域名时的提示页面状态码默认404。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly && httpAllDomainMismatchActionCode == 'page'">
<td class="color-border">提示页面内容</td>
<td>
<textarea rows="3" name="httpAllDomainMismatchActionContentHTML" v-model="httpAllDomainMismatchActionContentHTML"></textarea>
<p class="comment">访问未绑定的域名时提示页面内容可以使用HTML仅限于HTTP请求不适于用HTTPSHTTPS会提示证书错误</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly && httpAllDomainMismatchActionCode == 'redirect'">
<td class="color-border">跳转目标网址URL *</td>
<td>
<input type="text" placeholder="https://..." name="httpAllDomainMismatchActionRedirectURL" v-model="httpAllDomainMismatchActionRedirectURL"/>
<p class="comment">必须以http://或者https://开头,支持使用变量。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly">
<td>允许例外的域名</td>
<td>
<domains-box name="httpAllAllowMismatchDomainsJSON" :v-domains="config.httpAll.allowMismatchDomains"></domains-box>
<p class="comment">允许这些域名不经过绑定就可以直接访问网站。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly">
<td>默认域名</td>
<td>
<input type="text" name="httpAllDefaultDomain" v-model="config.httpAll.defaultDomain"/>
<p class="comment">例外域名或使用节点IP访问时使用的默认域名如果指定的域名在集群里已经绑定到某个网站则相当于直接访问该网站。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly">
<td class="color-border">允许使用节点IP访问</td>
<td>
<checkbox name="httpAllAllowNodeIP" v-model="config.httpAll.allowNodeIP"></checkbox>
<p class="comment">选中后表示允许直接使用节点IP访问网站。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly && config.httpAll.allowNodeIP">
<td class="color-border">访问节点IP显示自定义内容</td>
<td>
<checkbox name="httpAllNodeIPShowPage" v-model="config.httpAll.nodeIPShowPage"></checkbox>
<p class="comment">选中后表示用户访问节点IP时显示自定义内容。</p>
</td>
</tr>
<tr v-show="config.httpAll.matchDomainStrictly && config.httpAll.nodeIPShowPage">
<td class="color-border">访问节点IP自定义内容</td>
<td>
<textarea name="httpAllNodeIPPageHTML" v-model="config.httpAll.nodeIPPageHTML" rows="3" placeholder="访问节点IP时要显示的自定义内容"></textarea>
<p class="comment">访问节点IP时要显示的自定义内容支持HTML。</p>
</td>
</tr>
</table>
</div>
<div v-show="teaIsPlus && currentItem == 'title-server-name-auditing'">
<h4 id="title-server-name-auditing" v-if="teaIsPlus">域名审核</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">域名变更时需要审核</td>
<td>
<checkbox name="httpAllDomainAuditingIsOn" v-model="config.httpAll.domainAuditingIsOn"></checkbox>
<p class="comment">选中后,用户在创建和修改域名时需要管理员审核通过才能生效。</p>
</td>
</tr>
<tr v-show="config.httpAll.domainAuditingIsOn">
<td>审核提示</td>
<td>
<input name="httpAllDomainAuditingPrompt" type="text" v-model="config.httpAll.domainAuditingPrompt" maxlength="200"/>
<p class="comment">提示用户需要审核的文字说明。</p>
</td>
</tr>
</table>
</div>
<div v-show="currentItem == 'title-access-log'">
<h4 id="title-access-log">访问日志</h4>
<table class="ui table definition selectable">
<tr>
<td>允许记录访问日志</td>
<td>
<checkbox name="httpAccessLogIsOn" v-model="config.httpAccessLog.isOn"></checkbox>
<p class="comment">选中后,表示允许当前集群下的网站记录访问日志,否则当前集群下的所有网站都不会记录访问日志。</p>
</td>
</tr>
<tr>
<td>记录请求报头</td>
<td>
<checkbox name="httpAccessLogEnableRequestHeaders" v-model="config.httpAccessLog.enableRequestHeaders"></checkbox>
<p class="comment">选中后,表示在访问日志中记录请求报头。</p>
</td>
</tr>
<tr>
<td class="title">只记录通用请求报头</td>
<td>
<checkbox name="httpAccessLogCommonRequestHeadersOnly" v-model="config.httpAccessLog.commonRequestHeadersOnly"></checkbox>
<p class="comment">选中后表示访问日志中只记录通用的HTTP请求报头比如<code-label>User-Agent</code-label>),其他自定义或非标准的(比如<code-label>Test-Header</code-label>)将不记录。</p>
</td>
</tr>
<tr>
<td>记录响应报头</td>
<td>
<checkbox name="httpAccessLogEnableResponseHeaders" v-model="config.httpAccessLog.enableResponseHeaders"></checkbox>
<p class="comment">选中后,表示在访问日志中记录响应报头。</p>
</td>
</tr>
<tr>
<td>记录Cookie</td>
<td>
<checkbox name="httpAccessLogEnableCookies" v-model="config.httpAccessLog.enableCookies"></checkbox>
<p class="comment">选中后表示访问日志中记录Cookie内容。</p>
</td>
</tr>
<tr>
<td>记录找不到网站日志</td>
<td>
<checkbox name="httpAccessLogEnableServerNotFound" v-model="config.httpAccessLog.enableServerNotFound"></checkbox>
<p class="comment">选中后,表示如果访客访问的域名对应的网站不存在也会记录日志。</p>
</td>
</tr>
</table>
</div>
<div v-show="currentItem == 'title-server-log'">
<h4 id="title-server-log">运行日志</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">记录网站错误</td>
<td>
<checkbox name="logRecordServerError" v-model="config.log.recordServerError"></checkbox>
<p class="comment">在节点运行日志中记录网站相关错误详细信息,比如无法连接源站等,建议仅用于调试。</p>
</td>
</tr>
</table>
</div>
<div v-show="currentItem == 'title-performance'">
<h4 id="title-performance">性能</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">自动读数据超时</td>
<td>
<checkbox name="performanceAutoReadTimeout" v-model="config.performance.autoReadTimeout"></checkbox>
<p class="comment">从客户端读取数据时自动设置超时时间,如果超时,则自动视为慢连接,并关闭网络连接;此为专业选项,请在专家指导下进行修改。</p>
</td>
</tr>
<tr>
<td class="title">自动写数据超时</td>
<td>
<checkbox name="performanceAutoWriteTimeout" v-model="config.performance.autoWriteTimeout"></checkbox>
<p class="comment">向客户端发送数据时自动设置超时时间,如果超时,则自动视为慢连接,并关闭网络连接;此为专业选项,请在专家指导下进行修改。</p>
</td>
</tr>
<tr>
<td>调试模式</td>
<td>
<checkbox name="performanceDebug" v-model="config.performance.debug"></checkbox>
<p class="comment">开启调试模式后,将在某些信息中包含调试信息。</p>
</td>
</tr>
</table>
</div>
<div v-show="teaIsPlus && currentItem == 'title-ln'">
<h4 id="title-ln" v-if="teaIsPlus">Ln节点</h4>
<table class="ui table definition selectable">
<tr>
<td>强制Ln请求</td>
<td>
<checkbox name="httpAllForceLnRequest" v-model="config.httpAll.forceLnRequest"></checkbox>
<p class="comment">选中后所有请求优先发送到L2或者更高级别节点。默认不开启的情况下只有可以缓存的内容请求才会发送到L2或者更高级别节点。</p>
</td>
</tr>
<tr>
<td>Ln请求负载均衡方法</td>
<td>
<select class="ui dropdown auto-width" name="httpAllLnRequestSchedulingMethod" v-model="config.httpAll.lnRequestSchedulingMethod">
<option value="urlMapping">URL映射</option>
<option value="random">随机</option>
</select>
<p class="comment" v-if="config.httpAll.lnRequestSchedulingMethod == 'urlMapping'">当存在多个Ln节点时将请求根据URL自动映射到某个固定的Ln节点。</p>
<p class="comment" v-if="config.httpAll.lnRequestSchedulingMethod == 'random'">当存在多个Ln节点时将请求随机发送到某个Ln节点。</p>
</td>
</tr>
</table>
</div>
<div v-show="currentItem == 'title-tcp-udp'">
<h4 id="title-tcp-udp">TCP/UDP相关</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">允许的端口范围</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="tcpAllPortRangeMin" maxlength="5" size="6" v-model="tcpAllPortRangeMin"/>
</div>
<div class="ui field">
-
</div>
<div class="ui field">
<input type="text" name="tcpAllPortRangeMax" maxlength="5" size="6" v-model="tcpAllPortRangeMax"/>
</div>
</div>
<p class="comment">用户创建TCP/TLS负载均衡服务时可以随机选择的端口范围最小不能小于1024最大不能大于65534。</p>
</td>
</tr>
<tr>
<td>排除的端口</td>
<td>
<values-box placeholder="端口" size="6" name="tcpAllDenyPorts" :values="tcpAllDenyPorts"></values-box>
<p class="comment">当为用户随机分配端口时要排除的端口。</p>
</td>
</tr>
</table>
</div>
<div v-show="currentItem == 'title-others'">
<h4 id="title-others">其他</h4>
<table class="ui table definition selectable">
<tr>
<td class="title">服务器旗标</td>
<td>
<input type="text" name="httpAllServerName" v-model="config.httpAll.serverName" maxlength="60"/>
<p class="comment">服务器旗标会以<code-label>Server: 旗标</code-label>的形式出现在响应报头中。</p>
</td>
</tr>
<tr>
<td>支持低版本HTTP</td>
<td>
<checkbox name="httpAllSupportsLowVersionHTTP" v-model="config.httpAll.supportsLowVersionHTTP"></checkbox>
<p class="comment">选中后表示支持HTTP/1.0、HTTP/0.9等低于HTTP/1.1版本的HTTP协议。低版本HTTP协议不支持分段传输内容且无法保持连接对系统性能有严重的负面影响。建议只有在你的用户正在使用非常老旧的设备时才启用此选项。</p>
</td>
</tr>
<tr>
<td>自动匹配证书</td>
<td>
<checkbox name="httpAllMatchCertFromAllServers" v-model="config.httpAll.matchCertFromAllServers"></checkbox>
<p class="comment">选中后,表示找不到证书时自动查找其他网站设置的证书。此功能仅仅为了兼容以往系统版本,可能会导致用户访问的网站混乱,所以请不要轻易启用。</p>
</td>
</tr>
<tr>
<td>支持${serverAddr}变量</td>
<td>
<checkbox name="httpAllEnableServerAddrVariable" v-model="config.httpAll.enableServerAddrVariable"></checkbox>
<p class="comment">选中后,表示支持在自定义页面中使用<code-label>${serverAddr}</code-label>变量,用来表示访客当前正在访问的服务器地址。</p>
</td>
</tr>
<tr>
<td>自动gzip回源</td>
<td>
<checkbox name="httpAllRequestOriginsWithEncodings" v-model="config.httpAll.requestOriginsWithEncodings"></checkbox>
<p class="comment">选中后,表示自动使用<code-label>Accept-Encoding: gzip, ...</code-label>回源,可以用来节约源站流量。</p>
</td>
</tr>
<tr>
<td>XFF中最多地址数</td>
<td>
<input type="text" name="httpAllXFFMaxAddresses" v-model="config.httpAll.xffMaxAddresses"/>
<p class="comment">可以转发的X-Forwarded-For中最多的地址数如果为0表示不限制不要轻易修改此选项避免无法获取真实的客户端地址。</p>
</td>
</tr>
</table>
</div>
<div class="margin"></div>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,47 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
// Menu
this.currentItem = ""
this.titleMenus = []
this.$delay(function () {
let elements = document.querySelectorAll("#global-config-form h4")
this.currentItem = elements[0].getAttribute("id")
for (let i = 0; i < elements.length; i++) {
let textContent = elements[i].textContent
if (textContent == null || textContent.length == 0) {
textContent = elements[i].innerText
}
let itemId = elements[i].getAttribute("id")
if (window.location.hash == "#" + itemId) {
this.currentItem = itemId
}
this.titleMenus.push({
name: textContent,
id: itemId
})
}
})
this.selectItem = function (item) {
this.currentItem = item.id
}
/**
* TCP端口
*/
this.tcpAllPortRangeMin = 10000
this.tcpAllPortRangeMax = 40000
if (this.config.tcpAll.portRangeMin > 0) {
this.tcpAllPortRangeMin = this.config.tcpAll.portRangeMin
}
if (this.config.tcpAll.portRangeMax > 0) {
this.tcpAllPortRangeMax = this.config.tcpAll.portRangeMax
}
this.tcpAllDenyPorts = []
if (this.config.tcpAll.denyPorts != null) {
this.tcpAllDenyPorts = this.config.tcpAll.denyPorts
}
})

View File

@@ -0,0 +1,11 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="clusterId" :value="clusterId"/>
<health-check-config-box :v-health-check-config="healthCheckConfig" :v-check-domain-url="'/clusters/cluster/settings/health/checkDomain?clusterId=' + clusterId" :v-is-plus="teaIsPlus"></health-check-config-box>
<submit-btn></submit-btn> &nbsp; <a href="" @click.prevent="run()" v-if="healthCheckConfig != null && healthCheckConfig.isOn">立即检查</a>
</form>
</div>

View File

@@ -0,0 +1,11 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.run = function () {
teaweb.confirm("确定要对当前集群下的所有节点进行健康检查吗?", function () {
teaweb.popup("/clusters/cluster/settings/health/runPopup?clusterId=" + this.clusterId, {
height: "30em"
})
})
}
})

View File

@@ -0,0 +1,33 @@
{$layout "layout_popup"}
<h3>健康检查</h3>
<div v-show="!hasServers">
<span class="red">当前集群尚未部署网站,无法进行健康检查;请至少部署至少一个网站后再试。</span>
</div>
<div v-show="hasServers">
<span class="red" v-if="isRequesting">正在执行中,请等待执行完毕...</span>
<span class="red" v-if="!isRequesting && errorString.length > 0">{{errorString}}</span>
<form method="post" class="ui form" v-if="!isRequesting && errorString.length == 0">
<p>成功节点:<span class="green">{{countSuccess}}</span> &nbsp; 失败节点:<span class="red">{{countFail}}</span></p>
<table class="ui table selectable celled" v-if="results.length > 0">
<thead>
<tr>
<th>节点</th>
<th>结果</th>
<th nowrap="">耗时</th>
</tr>
</thead>
<tr v-for="result in results">
<td>{{result.node.name}}<span class="small" v-if="result.nodeAddr != null && result.nodeAddr.length > 0">{{result.nodeAddr}}</span></td>
<td>
<span v-if="!result.isOk" class="red">失败:{{result.error}}</span>
<span v-else class="green">成功</span>
</td>
<td>{{result.costMs}}ms</td>
</tr>
</table>
<button class="ui button primary" type="button" @click.prevent="success">完成</button>
</form>
</div>

View File

@@ -0,0 +1,47 @@
Tea.context(function () {
this.success = NotifyPopup
this.isRequesting = true
this.results = []
this.countSuccess = 0
this.countFail = 0
this.errorString = ""
this.$delay(function () {
if (this.hasServers) {
this.run()
}
})
this.run = function () {
this.isRequesting = true
this.errorString = ""
this.$post("$")
.params({
clusterId: this.clusterId
})
.timeout(60)
.success(function (resp) {
this.results = resp.data.results
let that = this
this.results.forEach(function (v) {
v.costMs = Math.ceil(v.costMs)
if (isNaN(v.costMs)) {
v.costMs = 0
}
if (v.isOk) {
that.countSuccess++
} else {
that.countFail++
}
})
})
.error(function () {
this.errorString = "执行健康检查超时,请重试"
})
.done(function () {
this.isRequesting = false
})
}
})

View File

@@ -0,0 +1,4 @@
<first-menu>
<menu-item :href="'.?clusterId=' + clusterId" code="index">设置</menu-item>
<menu-item :href="'.test?clusterId=' + clusterId" code="test">测试</menu-item>
</first-menu>

View File

@@ -0,0 +1,38 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<form class="ui form" method="post" 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>
<td class="title">启用HTTP/3功能</td>
<td>
<checkbox name="isOn" v-model="http3Policy.isOn"></checkbox>
<p class="comment">选中后表示当前集群下的网站可以使用HTTP/3功能单个网站可以在HTTPS设置页面里启用HTTP/3。</p>
</td>
</tr>
<tbody v-show="http3Policy.isOn">
<tr>
<td>UDP端口</td>
<td>
<input type="text" style="width: 5em" maxlength="4" name="port" v-model="http3Policy.port"/>
<p class="comment">HTTP/3通讯使用的UDP端口范围在1-1024请在服务器安全策略或者防火墙中设置此端口为通过。</p>
</td>
</tr>
<tr>
<td>支持手机浏览器</td>
<td>
<checkbox name="supportMobileBrowsers" v-model="http3Policy.supportMobileBrowsers"></checkbox>
<p class="comment">在手机浏览器上也尝试开启HTTP/3当下部分手机浏览器对HTTP/3支持的并不完善请根据自己的需要决定是否启用。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,59 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<div v-show="!http3Policy.isOn">
<div class="margin"></div>
<p class="ui message">当前集群尚未启用HTTP/3功能不需要做任何测试。</p>
</div>
<div v-show="http3Policy.isOn">
<div class="margin"></div>
<form class="ui form" data-tea-action="$" data-tea-before="before" data-tea-done="done" data-tea-success="success" data-tea-timeout="1800">
<input type="hidden" name="clusterId" :value="clusterId"/>
<input type="hidden" name="port" :value="http3Policy.port"/>
<table class="ui table definition selectable">
<tr>
<td class="title">测试网址 *</td>
<td>
<input type="text" name="url" ref="focus" placeholder="https://..."/>
<p class="comment">即待测试的网址,<strong>需要确保对应的网站已经启用了HTTP/3功能</strong>系统会自动访问每个节点上的网址内容以便确定HTTP/3是否在此节点上工作正常。</p>
</td>
</tr>
</table>
<submit-btn v-if="!isRequesting">开始测试</submit-btn>
<button class="ui button disabled" type="button" v-if="isRequesting">测试中,请耐心等待...</button>
</form>
<!-- 测试结果 -->
<div v-if="isTested">
<h4>测试结果 &nbsp; <a href="" @click.prevent="resetResults" style="font-size: 0.8em">[清除]</a> </h4>
<p class="comment" v-if="results.length == 0">暂时还没有任何可以测试的节点。</p>
<div v-if="results.length > 0">
<table class="ui table celled selectable" style="width: 40em">
<thead>
<tr>
<th style="width: 18em">节点</th>
<th style="width: 18em">IP</th>
<th>测试结果</th>
</tr>
</thead>
<tbody>
<tr v-for="result in results">
<td>{{result.nodeName}} <link-icon :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + result.nodeId"></link-icon></td>
<td>{{result.ip}}</td>
<td>
<span class="green" v-if="result.isOk">成功</span>
<span v-else class="red">失败 <tip-icon :content="result.error"></tip-icon></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,26 @@
Tea.context(function () {
this.isRequesting = false
this.isTested = false
this.results = []
this.before = function () {
this.isRequesting = true
this.isTested = false
this.results = []
}
this.success = function (resp) {
this.isTested = true
this.results = resp.data.results
}
this.done = function () {
this.isRequesting = false
}
this.resetResults = function () {
this.isTested = false
this.results = []
}
})

View File

@@ -0,0 +1,147 @@
{$layout}
{$template "menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<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 selectable definition">
<tr>
<td class="title">集群名称 *</td>
<td><input type="text" name="name" maxlength="50" ref="focus" v-model="cluster.name"/></td>
</tr>
<tr v-if="cluster.domainName.length > 0">
<td>域名</td>
<td>
{{cluster.domainName}}<link-icon :href="'/clusters/cluster/settings/dns?clusterId=' + cluster.id"></link-icon>
</td>
</tr>
<tr>
<td>默认SSH认证</td>
<td>
<grant-selector :v-grant="grant"></grant-selector>
<p class="comment">当节点没有单独设置SSH认证时默认使用此设置。</p>
</td>
</tr>
<tr>
<td>默认SSH端口</td>
<td>
<input type="text" name="sshParamsPort" v-model="cluster.sshParams.port" maxlength="5" style="width: 5em"/>
<p class="comment">节点默认SSH登录端口。</p>
</td>
</tr>
<tr>
<td>节点安装目录</td>
<td>
<input type="text" name="installDir" maxlength="100" v-model="cluster.installDir"/>
<p class="comment">当节点没有单独设置安装目录时,默认使用此设置。如果集群和节点都没有设置安装目录,则使用<span class="ui label tiny">$登录用户HOME路径/edge-node</span> 目录。</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 fields inline">
<div class="ui field">
<select class="ui dropdown" v-model="timeZoneGroupCode">
<option v-for="timeZoneGroup in timeZoneGroups" :value="timeZoneGroup.code">{{timeZoneGroup.name}}</option>
</select>
</div>
<div class="ui field">
<select class="ui dropdown" name="timeZone" v-model="cluster.timeZone">
<option v-for="timeZoneLocation in timeZoneLocations" :value="timeZoneLocation.name" v-if="timeZoneLocation.group == timeZoneGroupCode">{{timeZoneLocation.name}} ({{timeZoneLocation.offset}})</option>
</select>
</div>
</div>
<p class="comment">节点记录日志使用的时区。</p>
</td>
</tr>
<tr>
<td>单节点最大线程数</td>
<td>
<input type="text" name="nodeMaxThreads" maxlength="6" v-model="cluster.nodeMaxThreads"/>
<p class="comment">取值在{{defaultNodeMaxThreadsMin}}和{{defaultNodeMaxThreadsMax}}之间如果为0则默认为{{defaultNodeMaxThreads}}。</p>
</td>
</tr>
<tr>
<td>自动开放端口</td>
<td>
<checkbox name="autoOpenPorts" v-model="cluster.autoOpenPorts"></checkbox>
<p class="comment">选中后会自动尝试在边缘节点正在运行的firewalld中开放所需端口如果有别的防火墙或者安全策略仍然需要手工操作。</p>
</td>
</tr>
<tr>
<td class="color-border">自动同步节点时钟</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<checkbox name="clockAutoSync" v-model="cluster.clock.autoSync"></checkbox>
</div>
</div>
<p class="comment">选中后表示尝试自动同步节点时钟。</p>
</td>
</tr>
<tr v-show="cluster.clock.autoSync">
<td class="color-border">NTP时钟服务器</td>
<td>
<input type="text" name="clockServer" v-model="cluster.clock.server" maxlength="100"/>
<p class="comment">可选项。默认使用<code-label>pool.ntp.org</code-label></p>
</td>
</tr>
<tr v-show="cluster.clock.autoSync">
<td class="color-border">检查Chrony</td>
<td>
<checkbox name="clockCheckChrony" v-model="cluster.clock.checkChrony"></checkbox>
<p class="comment">选中后表示如果chrony另一款时间同步软件正在运行则不重复执行时间同步。</p>
</td>
</tr>
<tr>
<td>自动远程启动</td>
<td>
<checkbox name="autoRemoteStart" v-model="cluster.autoRemoteStart"></checkbox>
<p class="comment">当检测到节点离线时自动尝试远程启动前提是节点已经设置了SSH登录认证</p>
</td>
</tr>
<tr>
<td>自动安装nftables</td>
<td>
<checkbox name="autoInstallNftables" v-model="cluster.autoInstallNftables"></checkbox>
<p class="comment">选中后表示在Linux系统中自动尝试安装<code-label>nftables</code-label>用于安全防御此功能需要联网从Ubuntu、CentOS等软件库中下载安装包。</p>
</td>
</tr>
<tr>
<td>自动调节系统参数</td>
<td>
<checkbox name="autoSystemTuning" v-model="cluster.autoSystemTuning"></checkbox>
<p class="comment">选中后表示自动调整Linux内核等参数以便提升性能启用后以往安装的边缘节点需要重启进程后才能生效。</p>
</td>
</tr>
<tr>
<td>自动执行硬盘<br/>TRIM</td>
<td>
<checkbox name="autoTrimDisks" value="1" v-model="cluster.autoTrimDisks"></checkbox>
<p class="comment">执行TRIM后可以移除SSD硬盘中无效的数据从而显著提升SSD硬盘写入性能。</p>
</td>
</tr>
<tr>
<td>节点I/O最大读并发数</td>
<td>
<input name="maxConcurrentReads" v-model="cluster.maxConcurrentReads" type="text" style="width: 5em" maxlength="4"/>
<p class="comment">0表示根据系统资源自动计算通常不需要修改请在专业人士指导下操作。</p>
</td>
</tr>
<tr>
<td>节点I/O最大写并发数</td>
<td>
<input name="maxConcurrentWrites" v-model="cluster.maxConcurrentWrites" type="text" style="width: 5em" maxlength="4"/>
<p class="comment">0表示根据系统资源自动计算通常不需要修改请在专业人士指导下操作。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,35 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.timeZoneGroupCode = "asia"
if (this.timeZoneLocation != null) {
this.timeZoneGroupCode = this.timeZoneLocation.group
}
let oldTimeZoneGroupCode = this.timeZoneGroupCode
let oldTimeZoneName = ""
if (this.timeZoneLocation != null) {
oldTimeZoneName = this.timeZoneLocation.name
}
this.$delay(function () {
this.$watch("timeZoneGroupCode", function (groupCode) {
if (groupCode == oldTimeZoneGroupCode && oldTimeZoneName.length > 0) {
this.cluster.timeZone = oldTimeZoneName
return
}
let firstLocation = null
this.timeZoneLocations.forEach(function (v) {
if (firstLocation != null) {
return
}
if (v.group == groupCode) {
firstLocation = v
}
})
if (firstLocation != null) {
this.cluster.timeZone = firstLocation.name
}
})
})
})

View File

@@ -0,0 +1,21 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form 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 selectable definition">
<tr>
<td class="title">消息接收人</td>
<td>
<message-receivers-box :v-node-cluster-id="clusterId"></message-receivers-box>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,36 @@
{$layout "layout_popup"}
<h3>选择接收人</h3>
<form class="ui form">
<table class="ui table definition">
<tr>
<td class="title">选择接收人</td>
<td>
<div v-if="recipients.length == 0">
<span class="disabled">暂时没有接收人。</span>
</div>
<div v-else>
<a class="ui label small basic" :class="{blue: selectedRecipient != null && recipient.id == selectedRecipient.id}" v-for="recipient in recipients" @click.prevent="selectRecipient(recipient)">
{{recipient.name}} &nbsp; <span class="small grey">({{recipient.instanceName}})</span>
</a>
</div>
</td>
</tr>
<tr>
<td>选择接收人分组</td>
<td>
<div v-if="groups.length == 0">
<span class="disabled">暂时没有接收人分组。</span>
</div>
<div v-else>
<a class="ui label small basic" :class="{blue: selectedGroup != null && group.id == selectedGroup.id}" v-for="group in groups" @click.prevent="selectGroup(group)">
分组:{{group.name}}
</a>
</div>
</td>
</tr>
</table>
<button type="button" class="ui button primary" @click.prevent="confirm">确定</button>
</form>

View File

@@ -0,0 +1,40 @@
Tea.context(function () {
this.selectedRecipient = null
this.selectedGroup = null
this.selectRecipient = function (recipient) {
this.selectedRecipient = recipient
this.selectedGroup = null
}
this.selectGroup = function (group) {
this.selectedRecipient = null
this.selectedGroup = group
}
this.confirm = function () {
if (this.selectedRecipient != null) {
NotifyPopup({
code: 200,
data: {
id: this.selectedRecipient.id,
name: this.selectedRecipient.name,
subName: this.selectedRecipient.instanceName,
type: "recipient"
}
})
} else if (this.selectedGroup != null) {
NotifyPopup({
code: 200,
data: {
id: this.selectedGroup.id,
name: this.selectedGroup.name,
type: "group"
}
})
} else {
teaweb.closePopup()
}
}
})

View File

@@ -0,0 +1,43 @@
{$layout "layout_popup"}
<h3>可以添加的指标</h3>
<p class="comment" v-if="items.length == 0">暂时还没有可用的指标。</p>
<table class="ui table celled selectable" v-if="items.length > 0">
<thead>
<tr>
<th>指标名称</th>
<th>统计对象</th>
<th>统计周期</th>
<th class="three wide">统计数值</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="item in items">
<td>{{item.name}}
<div style="margin-top: 0.3em" v-if="item.isPublic || item.code.length > 0">
<span class="ui label olive tiny basic" v-if="item.isPublic">公用</span>
<span class="ui label olive tiny basic" v-if="item.code.length > 0">内置</span>
</div>
</td>
<td>
<div v-if="item.keys != null" v-for="key in item.keys" style="margin-top: 0.2em; margin-bottom: 0.2em"><metric-key-label :v-key="key"></metric-key-label></div>
</td>
<td>
{{item.period}} {{item.periodUnitName}}
</td>
<td>
<span class="ui label small basic">{{item.valueName}}</span>
</td>
<td>
<label-on :v-is-on="item.isOn"></label-on>
</td>
<td>
<a href="" @click.prevent="addItem(item)" v-if="!item.isChecked">添加</a>
<a href="" @click.prevent="removeItem(item)" v-if="item.isChecked"><span class="grey">已添加</span></a>
</td>
</tr>
</table>
<div class="page" v-html="page"></div>

View File

@@ -0,0 +1,23 @@
Tea.context(function () {
this.addItem = function (item) {
this.$post("$")
.params({
clusterId: this.clusterId,
itemId: item.id
})
.success(function () {
item.isChecked = true
})
}
this.removeItem = function (item) {
this.$post(".delete")
.params({
clusterId: this.clusterId,
itemId: item.id
})
.success(function () {
item.isChecked = false
})
}
})

View File

@@ -0,0 +1,53 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item :href="'/clusters/cluster/settings/metrics?category=http&clusterId=' + clusterId" :active="category == 'http'">HTTP</menu-item>
<!--<menu-item :href="'/clusters/cluster/settings/metrics?category=tcp&clusterId=' + clusterId" :active="category == 'tcp'">TCP</menu-item>
<menu-item :href="'/clusters/cluster/settings/metrics?category=udp&clusterId=' + clusterId" :active="category == 'udp'">UDP</menu-item>-->
<span class="item disabled">|</span>
<menu-item @click.prevent="createItem">[添加指标]</menu-item>
<span class="item disabled">|</span>
<span class="item"><tip-icon content="在这里设置的指标,会自动应用到部署在当前集群上的所有网站上。<br/><br/>指标收集和运算通常需要消耗一定量的边缘节点系统资源,所以请谨慎选择。"></tip-icon></span>
</first-menu>
<p class="comment" v-if="items.length == 0">暂时还没有添加指标。</p>
<table class="ui table celled selectable" v-if="items.length > 0">
<thead>
<tr>
<th>指标名称</th>
<th>统计对象</th>
<th>统计周期</th>
<th class="three wide">统计数值</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="item in items">
<td>{{item.name}}
<div style="margin-top: 0.3em" v-if="item.isPublic || item.code.length > 0">
<span class="ui label olive tiny basic" v-if="item.isPublic">公用</span>
<span class="ui label olive tiny basic" v-if="item.code.length > 0">内置</span>
</div>
</td>
<td>
<div v-if="item.keys != null" v-for="key in item.keys" style="margin-top: 0.2em; margin-bottom: 0.2em"><metric-key-label :v-key="key"></metric-key-label></div>
</td>
<td>
{{item.period}} {{item.periodUnitName}}
</td>
<td>
<span class="ui label small basic">{{item.valueName}}</span>
</td>
<td>
<label-on :v-is-on="item.isOn"></label-on>
</td>
<td>
<a href="" @click.prevent="deleteItem(item.id)">删除</a>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,35 @@
Tea.context(function () {
this.createItem = function () {
teaweb.popup(Tea.url(".createPopup", {
clusterId: this.clusterId,
category: this.category
}), {
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
},
onClose: function () {
teaweb.reload()
},
width: "50em",
height: "25em"
})
}
this.deleteItem = function (itemId) {
let that = this
teaweb.confirm("确定要删除这个指标吗?", function () {
that.$post(".delete")
.params({
clusterId: that.clusterId,
itemId: itemId
})
.success(function () {
teaweb.success("删除成功", function () {
teaweb.reload()
})
})
})
}
})

View File

@@ -0,0 +1,32 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div class="margin"></div>
<warning-message>此功能为试验功能,目前只能做一些简单的网络数据包统计。</warning-message>
<form 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>
<td class="title">启用数据包统计</td>
<td>
<select class="ui dropdown auto-width" name="status" v-model="policy.status">
<option value="auto">自动</option>
<option value="on">启用</option>
<option value="off">停用</option>
</select>
<p class="comment" v-if="policy.status == 'auto'">自动根据服务器硬件配置决定是否启用,避免影响性能。</p>
<p class="comment" v-if="policy.status == 'on'">强制启用如果节点服务器配置较低可能会严重影响性能建议仅在8线程以上CPU节点服务器上选择强制启用。</p>
<p class="comment" v-if="policy.status == 'off'">完全关闭此功能。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,25 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">启用自定义页面功能</td>
<td>
<checkbox name="isOn" v-model="pagesPolicy.isOn"></checkbox>
<p class="comment">选中后,表示当前集群下的网站都可以使用当前设置的自定义页面。</p>
</td>
</tr>
</table>
<div v-show="pagesPolicy.isOn">
<http-pages-box :v-pages="pagesPolicy.pages"></http-pages-box>
</div>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,52 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<p class="comment" v-if="nodes.length == 0">暂时还没有节点。</p>
<div v-if="nodes.length > 0">
<div class="margin"></div>
<table class="ui table selectable celled">
<thead>
<tr>
<th>节点</th>
<th>租期结束日期</th>
<th>所属集群</th>
<th class="two wide">是否为<br/>集群备用节点</th>
<th>所属分组</th>
<th class="two wide">是否为<br/>分组备用节点</th>
<th>当前状态</th>
<th class="one op">操作</th>
</tr>
</thead>
<tr v-for="node in nodes">
<td>{{node.nodeName}}</td>
<td>
<span v-if="node.offlineDay.length > 0" :class="{red: node.isOffline}">{{node.offlineDay.substring(0, 4)}}-{{node.offlineDay.substring(4, 6)}}-{{node.offlineDay.substring(6, 8)}}</span>
<span v-else class="disabled">没有设置</span>
</td>
<td>{{node.clusterName}}</td>
<td>
<span v-if="node.isBackupForCluster" class="green">备用</span>
<span v-else class="disabled">N</span>
</td>
<td>
<span v-if="node.groupName.length > 0">{{node.groupName}}</span>
<span v-else class="disabled">没有设置</span>
</td>
<td>
<span v-if="node.isBackupForGroup" class="green">备用</span>
<span v-else class="disabled">N</span>
</td>
<td>
<a v-if="node.actionStatus != null && node.actionStatus.actionId > 0" :href="'/clusters/cluster/node/settings/schedule?clusterId=' + node.clusterId + '&nodeId=' + node.nodeId" target="_blank"><span class="red" style="border-bottom: 1px red dashed">已触发</span></a>
<span v-else class="disabled">未触发</span>
</td>
<td>
<a :href="'/clusters/cluster/node/settings/schedule?clusterId=' + node.clusterId + '&nodeId=' + node.nodeId" target="_blank">修改</a>
</td>
</tr>
</table>
</div>
</div>

View File

@@ -0,0 +1,4 @@
<first-menu>
<menu-item :href="'/clusters/cluster/settings/services?clusterId=' + clusterId" code="setting">设置</menu-item>
<menu-item :href="'/clusters/cluster/settings/services/status?clusterId=' + clusterId" code="status">状态</menu-item>
</first-menu>

View File

@@ -0,0 +1,24 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="clusterId" :value="clusterId"/>
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">自动加入Systemd服务</td>
<td>
<checkbox name="systemdIsOn" v-model="systemdIsOn"></checkbox>
<p class="comment">加入后可以利用systemd对节点进程进行管理并可自动随开机启动。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,38 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<div v-if="isRequesting" class="ui message">正在节点服务状态检测中,请稍候...</div>
<div v-if="!isRequesting">
<div v-if="results.length == 0">暂时还没有节点。</div>
<div v-else>
<div class="margin"></div>
<table class="ui table selectable">
<thead>
<tr>
<th>节点名</th>
<th>是否已启用服务</th>
<th>提示消息</th>
</tr>
</thead>
<tr v-for="result in results">
<td>{{result.nodeName}}</td>
<td>
<span v-if="result.isOk" class="green">Y</span>
<span v-else class="red">N</span>
</td>
<td>
<span :class="{red: !result.isOk}">{{result.message}}</span>
</td>
</tr>
</table>
<p class="comment">
<a href="" @click.prevent="reload()">[刷新]</a>
</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,22 @@
Tea.context(function () {
this.isRequesting = true
this.results = []
this.$delay(function () {
this.reload()
}, 2000)
this.reload = function () {
this.isRequesting = true
this.$post("$")
.params({
clusterId: this.clusterId
})
.success(function (resp) {
this.results = resp.data.results
})
.done(function () {
this.isRequesting = false
})
}
})

View File

@@ -0,0 +1,90 @@
{$layout "layout_popup"}
<h3>添加阈值</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="clusterId" :value="clusterId" />
<input type="hidden" name="nodeId" :value="nodeId"/>
<input type="hidden" name="sumMethod" value="avg"/>
<table class="ui table definition selectable">
<tr>
<td class="title">监控项 *</td>
<td>
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
<option v-for="item in items" :value="item.code">{{item.name}}</option>
</select>
<p class="comment">{{itemDescription}}</p>
</td>
</tr>
<tr>
<td>参数 *</td>
<td>
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
</select>
<p class="comment">{{paramDescription}}</p>
</td>
</tr>
<tr>
<td>操作符 *</td>
<td>
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
</select>
</td>
</tr>
<tr>
<td>对比值 *</td>
<td>
<div class="ui input" :class="{'right labeled': paramIsPercent}">
<input type="text" name="value" style="width: 6em" maxlength="10"/>
<span class="ui label" v-if="paramIsPercent">%</span>
</div>
</td>
</tr>
<tr>
<td>统计时间段 *</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="duration" value="5" style="width: 5em"/>
</div>
<div class="ui field">
分钟
<!-- TODO 将来支持更多时间范围 -->
<input type="hidden" name="durationUnit" value="minute"/>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>消息</td>
<td>
<textarea rows="2" maxlength="100" name="message"></textarea>
<p class="comment">触发阈值时的消息提示。</p>
</td>
</tr>
<tr>
<td>消息通知间隔</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="notifyDuration" value="10" style="width: 5em"/>
</div>
<div class="ui field">
分钟
</div>
</div>
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,46 @@
Tea.context(function () {
this.success = NotifyPopup
this.threshold = {
item: this.items[0].code,
param: "",
operator: this.operators[0].code
}
this.$delay(function () {
this.changeItem()
this.changeParam()
})
this.itemDescription = ""
this.itemParams = []
this.changeItem = function () {
let that = this
this.threshold.param = ""
this.items.forEach(function (v) {
if (v.code == that.threshold.item) {
that.itemDescription = v.description
that.itemParams = v.params
that.threshold.param = v.params[0].code
that.paramDescription = v.params[0].description
that.paramIsPercent = v.params[0].isPercent
}
})
}
this.paramDescription = ""
this.paramIsPercent = false
this.changeParam = function () {
let that = this
this.items.forEach(function (v) {
if (v.code == that.threshold.item) {
v.params.forEach(function (param) {
if (param.code == that.threshold.param) {
that.paramDescription = param.description
that.paramIsPercent = param.isPercent
}
})
}
})
}
})

View File

@@ -0,0 +1,42 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item @click.prevent="createThreshold">[添加阈值]</menu-item>
</first-menu>
<p class="comment" v-if="thresholds.length == 0">暂时还没有设置阈值。</p>
<table class="ui table selectable celled" v-if="thresholds.length > 0">
<thead>
<tr>
<th>监控项</th>
<th>参数</th>
<th>操作符</th>
<th>对比值</th>
<th>统计时间段</th>
<th class="two wide">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="threshold in thresholds">
<td>{{threshold.itemName}}
<div v-if="threshold.node != null" style="margin-top: 0.3em">
<a :href="'/clusters/cluster/node/settings/thresholds?clusterId=' + clusterId + '&nodeId=' + threshold.node.id" class="ui label basic tiny" title="节点专属阈值设置"><span class="small">节点:{{threshold.node.name}}</span></a>
</div>
</td>
<td>{{threshold.paramName}}</td>
<td>{{threshold.operatorName}}</td>
<td>{{threshold.value}}<span v-if="threshold.paramIsPercent">%</span></td>
<td>{{threshold.duration}}{{threshold.durationUnitName}}</td>
<td>
<label-on :v-is-on="threshold.isOn"></label-on>
</td>
<td>
<a href="" @click.prevent="updateThreshold(threshold.id)">修改</a> &nbsp;
<a href="" @click.prevent="deleteThreshold(threshold.id)">删除</a>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,42 @@
Tea.context(function () {
this.createThreshold = function () {
teaweb.popup(Tea.url(".createPopup", {
clusterId: this.clusterId
}), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.updateThreshold = function (thresholdId) {
teaweb.popup(Tea.url(".updatePopup", {
thresholdId: thresholdId
}), {
height: "30em",
callback: function () {
teaweb.success("保存成功", function () {
teaweb.reload()
})
}
})
}
this.deleteThreshold = function (thresholdId) {
let that = this
teaweb.confirm("确定要删除这个阈值吗?", function () {
that.$post(".delete")
.params({
thresholdId: thresholdId
})
.success(function () {
teaweb.success("删除成功", function () {
teaweb.reload()
})
})
})
}
})

View File

@@ -0,0 +1,95 @@
{$layout "layout_popup"}
<h3>修改阈值</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="thresholdId" :value="threshold.id"/>
<input type="hidden" name="sumMethod" value="avg"/>
<table class="ui table definition selectable">
<tr>
<td class="title">监控项 *</td>
<td>
<select class="ui dropdown auto-width" name="item" v-model="threshold.item" @change="changeItem">
<option v-for="item in items" :value="item.code">{{item.name}}</option>
</select>
<p class="comment">{{itemDescription}}</p>
</td>
</tr>
<tr>
<td>参数 *</td>
<td>
<select class="ui dropdown auto-width" name="param" v-model="threshold.param" @change="changeParam">
<option v-for="param in itemParams" :value="param.code">{{param.name}}</option>
</select>
<p class="comment">{{paramDescription}}</p>
</td>
</tr>
<tr>
<td>操作符 *</td>
<td>
<select class="ui dropdown auto-width" name="operator" v-model="threshold.operator">
<option v-for="operator in operators" :value="operator.code">{{operator.name}}</option>
</select>
</td>
</tr>
<tr>
<td>对比值 *</td>
<td>
<div class="ui input" :class="{'right labeled': paramIsPercent}">
<input type="text" name="value" style="width: 6em" maxlength="10" v-model="threshold.value"/>
<span class="ui label" v-if="paramIsPercent">%</span>
</div>
</td>
</tr>
<tr>
<td>统计时间段 *</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="duration" value="5" style="width: 5em" v-model="threshold.duration"/>
</div>
<div class="ui field">
分钟
<!-- TODO 将来支持更多时间范围 -->
<input type="hidden" name="durationUnit" value="minute" v-model="threshold.durationUnit"/>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>消息</td>
<td>
<textarea rows="2" maxlength="100" name="message" v-model="threshold.message"></textarea>
<p class="comment">触发阈值时的消息提示。</p>
</td>
</tr>
<tr>
<td>消息通知间隔</td>
<td>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="notifyDuration" v-model="threshold.notifyDuration" value="10" style="width: 5em"/>
</div>
<div class="ui field">
分钟
</div>
</div>
<p class="comment">在此间隔内将不会重复发送跟此阈值相关的消息。</p>
</td>
</tr>
<tr>
<td>启用当前阈值</td>
<td>
<checkbox name="isOn" value="1" v-model="threshold.isOn"></checkbox>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,51 @@
Tea.context(function () {
this.success = NotifyPopup
this.$delay(function () {
this.initItem()
this.changeParam()
})
this.itemDescription = ""
this.itemParams = []
this.initItem = function () {
let that = this
this.items.forEach(function (v) {
if (v.code == that.threshold.item) {
that.itemDescription = v.description
that.itemParams = v.params
}
})
}
this.changeItem = function () {
let that = this
this.threshold.param = ""
this.items.forEach(function (v) {
if (v.code == that.threshold.item) {
that.itemDescription = v.description
that.itemParams = v.params
that.threshold.param = v.params[0].code
that.paramDescription = v.params[0].description
that.paramIsPercent = v.params[0].isPercent
}
})
}
this.paramDescription = ""
this.paramIsPercent = false
this.changeParam = function () {
let that = this
this.items.forEach(function (v) {
if (v.code == that.threshold.item) {
v.params.forEach(function (param) {
if (param.code == that.threshold.param) {
that.paramDescription = param.description
that.paramIsPercent = param.isPercent
}
})
}
})
}
})

View File

@@ -0,0 +1,61 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<first-menu>
<menu-item><tip-icon content="TCP Option Address(TOA)可以在TCP选项中传递客户端IP多用在TCP负载均衡的源站需要获取客户端真实IP的场景。<br/><br/>注意HTTP协议通常不需要此设置。<br/><br/>如需修改配置,请在专业人士指导下操作。"></tip-icon></menu-item>
<a class="item" href="https://goedge.cn/docs/Node/toa.md" target="_blank">文档 &nbsp; <i class="icon external small"></i></a>
</first-menu>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="clusterId" :value="clusterId"/>
<csrf-token></csrf-token>
<table class="ui table selectable definition">
<tr>
<td class="title">启用TOA</td>
<td>
<checkbox name="isOn" v-model="toa.isOn"></checkbox>
<p class="comment">在启用之前请确保当前集群下所有节点服务器安装并启用了nftables。</p>
</td>
</tr>
<tbody v-show="toa.isOn">
<tr>
<td class="title">IPv4选项类型数值</td>
<td>
<input type="text" name="optionTypeV4" v-model="toa.optionTypeV4" style="width:4em" maxlength="3"/>
<p class="comment">用来作为附加的IPv4 TCP OPTION值通常是254即0xfe请查阅云服务商文档来确认此值。</p>
</td>
</tr>
<tr>
<td class="title">IPv6选项类型数值</td>
<td>
<input type="text" name="optionTypeV6" v-model="toa.optionTypeV6" style="width:4em" maxlength="3"/>
<p class="comment">用来作为附加的IPv6 TCP OPTION值通常是254即0xfe请查阅云服务商文档来确认此值。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
</tbody>
<tbody v-show="toa.isOn && moreOptionsVisible">
<tr>
<td>自动配置</td>
<td>
<checkbox name="autoSetup" v-model="toa.autoSetup"></checkbox>
<p class="comment">TOA功能需要节点服务器安装并开启了nftables并将网络数据包转发到NFQueue中。如果选中了自动配置则每次在启动时都会自动尝试配置nftables规则。</p>
</td>
</tr>
<tr>
<td>NFQueue队列开始ID</td>
<td>
<input type="text" name="minQueueId" v-model="toa.minQueueId" style="width:4em" maxlength="3"/>
<p class="comment">不能超过255。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,103 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">启用5秒盾功能</td>
<td>
<checkbox name="isOn" v-model="uamPolicy.isOn"></checkbox>
<p class="comment">选中后表示当前集群下的网站可以使用5秒盾功能。</p>
</td>
</tr>
<tbody v-show="uamPolicy.isOn">
<tr>
<td>验证有效期</td>
<td>
<div class="ui input right labeled">
<input type="text" name="keyLife" v-model="uamPolicy.keyLife" maxlength="6" size="6" style="width: 6em"/>
<span class="ui label"></span>
</div>
<p class="comment">单个客户端验证通过后在这个有效期内不再重复验证如果为0则表示默认3600秒。</p>
</td>
</tr>
<tr>
<td>拦截时长</td>
<td>
<div class="ui input right labeled">
<input type="text" name="blockSeconds" v-model="uamPolicy.blockSeconds" maxlength="6" size="6" style="width: 6em"/>
<span class="ui label"></span>
</div>
<p class="comment">验证失败后,系统拦截的时间。</p>
</td>
</tr>
<tr>
<td>最大失败次数</td>
<td>
<div class="ui input right labeled">
<input type="text" name="maxFails" v-model="uamPolicy.maxFails" maxlength="6" size="6" style="width: 6em"/>
<span class="ui label"></span>
</div>
<p class="comment">失败尝试达到此数字后,则认为验证失败。</p>
</td>
</tr>
<tr>
<td>页面标题</td>
<td>
<input type="text" maxlength="200" name="uiTitle" v-model="uamPolicy.uiTitle"/>
<p class="comment">提示页面的标题。</p>
</td>
</tr>
<tr>
<td>页面提示内容</td>
<td>
<textarea rows="8" name="uiBody" v-model="uamPolicy.uiBody"></textarea>
<p class="comment">提示页面的HTML内容支持请求变量比如<code-label>${host}</code-label>表示正在访问的域名,<code-label>${product.name}</code-label>表示当前系统产品名称),留空表示使用默认提示内容;用<code-label>&lt;span class=&quot;ui-counter&quot;&gt;5&lt;/span&gt;</code-label>表示数字倒计时计数器。<a href="" @click.prevent="setTemplateBody">[使用模板]</a></p>
</td>
</tr>
</tbody>
<tr v-if="uamPolicy.isOn">
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>允许常见的搜索引擎</td>
<td>
<checkbox name="allowSearchEngines" v-model="uamPolicy.allowSearchEngines"></checkbox>
<p class="comment">选中表示允许Google、Bing等常见搜索引擎直接跳过检查。</p>
</td>
</tr>
<tr>
<td>拦截常见爬虫</td>
<td>
<checkbox name="denySpiders" v-model="uamPolicy.denySpiders"></checkbox>
<p class="comment">选中表示自动拦截Python、Scrapy等常见爬虫。</p>
</td>
</tr>
<tr>
<td>包含子域名</td>
<td>
<checkbox name="includeSubdomains" v-model="uamPolicy.includeSubdomains"></checkbox>
<p class="comment">选中后,表示子域名之间只需验证一次。不要轻易修改此选项,防止导致短时间内大量用户验证信息失效。</p>
</td>
</tr>
<tr>
<td>防火墙拦截范围</td>
<td>
<select name="firewallScope" v-model="uamPolicy.firewall.scope" class="ui dropdown auto-width">
<option value="global">所有网站</option>
<option value="service">当前网站</option>
</select>
<p class="comment" v-if="uamPolicy.firewall.scope == 'global'">“所有网站”表示IP被拦截时将被阻止访问所有网站此时系统可以自动尝试利用本地防火墙提升拦截效率。</p>
<p class="comment" v-if="uamPolicy.firewall.scope == 'service'">“当前网站”表示IP被拦截时只会阻止访问当前网站选择此选项会严重影响拦截效率建议只有特殊情况下才使用。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,12 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.setTemplateBody = function () {
this.uamPolicy.uiBody = `<div class="ui-uam-box">
<h1>Checking your browser before accessing \${host}</h1>
<p>Please allow up to <span class="ui-counter">5</span> seconds ...</p>
<p>&nbsp;</p>
<p>DDoS protection by \${product.name}</p>
</div>`
}
})

View File

@@ -0,0 +1,20 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">选择WAF策略 *</td>
<td>
<http-firewall-policy-selector :v-http-firewall-policy="firewallPolicy"></http-firewall-policy-selector>
<p class="comment">部署在当前集群上的所有网站都会自动使用此策略,只是网站可以自定义是否开启。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})

View File

@@ -0,0 +1,53 @@
{$layout}
{$template "../menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" method="post" 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>
<td class="title">启用WebP功能</td>
<td>
<checkbox name="isOn" v-model="webpPolicy.isOn"></checkbox>
<p class="comment">选中后表示当前集群下的网站可以使用WebP转换功能。</p>
</td>
</tr>
<tbody v-show="webpPolicy.isOn">
<tr>
<td>图片质量</td>
<td>
<div class="ui input right labeled">
<input type="text" name="quality" v-model="webpPolicy.quality" style="width: 5em" maxlength="4"/>
<span class="ui label">%</span>
</div>
<p class="comment">取值在0到100之间0表示自动调节数值越大生成的图像越清晰文件尺寸也会越大同时消耗的系统资源越多在低配置的边缘节点上建议不超过50%)。</p>
</td>
</tr>
<tr>
<td>需要满足缓存条件</td>
<td>
<checkbox name="requireCache" v-model="webpPolicy.requireCache"></checkbox>
<p class="comment">选中后表示图片URL需要满足缓存条件后才会转换防止实时转换消耗巨大的系统资源。</p>
</td>
</tr>
<tr>
<td>可转换的最小尺寸</td>
<td>
<size-capacity-box :v-name="'minLengthJSON'" :v-value="webpPolicy.minLength"></size-capacity-box>
<p class="comment">不低于此尺寸的图像文件的才会转换。</p>
</td>
</tr>
<tr>
<td>可转换的最大尺寸</td>
<td>
<size-capacity-box :v-name="'maxLengthJSON'" :v-value="webpPolicy.maxLength"></size-capacity-box>
<p class="comment">不高于此尺寸的图像文件的才会转换。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,3 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
})