This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
.chart-box {
height: 14em;
}
.traffic-map-box {
height: 16em;
}
.traffic-map-box div::-webkit-scrollbar {
width: 4px;
}
h4 span {
font-size: 0.8em;
color: grey;
font-weight: normal !important;
}
.menu .item span.small {
font-size: 0.8em;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,YAAA;;AAGD;EACC,YAAA;;AADD,gBAGC,IAAG;EACF,UAAA;;AAIF,EACC;EACC,gBAAA;EACA,WAAA;EACA,8BAAA;;AAIF,KACC,MAAM,KAAI;EACT,gBAAA","file":"index.css"}

View File

@@ -0,0 +1,122 @@
{$layout}
{$var "header"}
<!-- world map -->
<script type="text/javascript" src="/js/echarts/echarts.min.js"></script>
<script type="text/javascript" src="/js/world.js"></script>
<script type="text/javascript" src="/js/world-countries-map.js"></script>
{$end}
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server?serverId=' + server.id" active="true">"{{server.name}}"看板</menu-item>
<span class="disabled item">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/boards?serverId=${serverId}'"></more-items-angle>
</first-menu>
<!-- 加载中 -->
<div style="margin-top: 0.8em">
<div class="ui message loading" v-if="isLoading">
<div class="ui active inline loader small"></div> &nbsp; 数据加载中...
</div>
</div>
<!-- 总体统计 -->
<columns-grid v-if="!isLoading">
<div class="ui column">
<h4>上月峰值带宽</h4>
<div class="value"><bits-var :v-bits="lastMonthlyPeekBandwidthBits"></bits-var></div>
</div>
<div class="ui column">
<h4>当月峰值带宽</h4>
<div class="value"><bits-var :v-bits="monthlyPeekBandwidthBits"></bits-var></div>
</div>
<div class="ui column">
<h4>当天峰值带宽</h4>
<div class="value"><bits-var :v-bits="dailyPeekBandwidthBits"></bits-var></div>
</div>
<div class="ui column">
<h4>当前峰值带宽</h4>
<div class="value"><bits-var :v-bits="minutelyPeekBandwidthBits"></bits-var></div>
</div>
<div class="ui column">
<h4>当天独立IP</h4>
<div class="value">
<span v-if="dailyCountIPs > 0">{{dailyCountIPsFormat}}</span>
<span v-else class="disabled">尚无数据</span>
</div>
</div>
<div class="ui column">
<h4>当天流量</h4>
<div class="value"><bytes-var :v-bytes="dailyTrafficBytes"></bytes-var></div>
</div>
</columns-grid>
<chart-columns-grid>
<div class="ui column">
<!-- 流量地图 -->
<div v-if="!isLoading">
<traffic-map-box :v-stats="topCountryStats"></traffic-map-box>
</div>
</div>
<div class="ui column">
<!-- 带宽图表 -->
<div class="ui menu text blue" v-show="!isLoading">
<a href="" class="item" :class="{active: bandwidthTab == 'minutely'}" @click.prevent="selectBandwidthTab('minutely')">最近带宽<span class="small grey">(比特)</span></a>
<a href="" class="item" :class="{active: bandwidthTab == 'hourly'}" @click.prevent="selectBandwidthTab('hourly')">按小时带宽<span class="small grey">(比特)</span></a>
<a href="" class="item" :class="{active: bandwidthTab == 'daily'}" @click.prevent="selectBandwidthTab('daily')">按天带宽<span class="small grey">(比特)</span></a>
<a :href="'/servers/traffic-stats?serverId=' + server.id" class="item right">更多</a>
</div>
<div class="ui divider"></div>
<div class="chart-box" id="bandwidth-chart"></div>
</div>
<div class="ui column">
<!-- 流量图表 -->
<div class="ui menu text blue" v-show="!isLoading">
<a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时流量趋势</a>
<a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天流量趋势</a>
</div>
<div class="ui divider"></div>
<!-- 按小时统计流量 -->
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
<!-- 按日统计流量 -->
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
</div>
<div class="ui column">
<div class="ui menu text blue" v-show="!isLoading">
<a href="" class="item" :class="{active: requestsTab == 'hourly'}" @click.prevent="selectRequestsTab('hourly')">24小时访问量趋势</a>
<a href="" class="item" :class="{active: requestsTab == 'daily'}" @click.prevent="selectRequestsTab('daily')">15天访问量趋势</a>
</div>
<div class="ui divider"></div>
<!-- 按小时统计访问量 -->
<div class="chart-box" id="hourly-requests-chart" v-show="requestsTab == 'hourly'"></div>
<!-- 按日统计访问量 -->
<div class="chart-box" id="daily-requests-chart" v-show="requestsTab == 'daily'"></div>
</div>
<div class="ui column">
<!-- 域名排行 -->
<h4 v-show="!isLoading">域名访问排行 <span>24小时</span></h4>
<div class="ui divider"></div>
<div class="chart-box" id="top-domains-chart"><loading-message>数据加载中...</loading-message></div>
</div>
<!-- 指标 -->
<metric-chart v-for="chart in metricCharts"
:key="chart.id"
:v-chart="chart.chart"
:v-stats="chart.stats"
:v-item="chart.item"
:v-column="true">
</metric-chart>
</chart-columns-grid>

View File

@@ -0,0 +1,498 @@
Tea.context(function () {
this.isLoading = true
this.metricCharts = []
this.formatBytes = teaweb.formatBytes
this.dailyCountIPsFormat = "0"
/**
* 流量统计
*/
this.trafficTab = "hourly"
this.$delay(function () {
this.load()
})
this.load = function () {
this.$post("$")
.params({
serverId: this.server.id
})
.success(function (resp) {
for (let k in resp.data) {
this[k] = resp.data[k]
}
this.isLoading = false
if (this.dailyCountIPs > 0) {
this.dailyCountIPsFormat = teaweb.formatNumber(this.dailyCountIPs)
}
this.$delay(function () {
this.reloadMinutelyBandwidthChart()
this.reloadHourlyTrafficChart()
this.reloadHourlyRequestsChart()
})
// 域名统计
this.$post(".domainStats")
.params({
serverId: this.server.id
})
.success(function (resp) {
for (let k in resp.data) {
this[k] = resp.data[k]
}
this.reloadTopDomainsChart()
})
})
}
this.bandwidthTab = "minutely"
this.hourlyBandwidthStats = []
this.dailyBandwidthStats = []
this.selectBandwidthTab = function (tab) {
this.bandwidthTab = tab
switch (tab) {
case "minutely":
this.reloadMinutelyBandwidthChart()
break
case "hourly":
this.$post(".hourlyBandwidth")
.params({
serverId: this.serverId
})
.success(function (response) {
this.hourlyBandwidthStats = response.data.stats
this.reloadHourlyBandwidthChart(response.data.percentile, response.data.percentileBits)
})
break
case "daily":
this.$post(".dailyBandwidth")
.params({
serverId: this.serverId
})
.success(function (response) {
this.dailyBandwidthStats = response.data.stats
this.reloadDailyBandwidthChart(response.data.percentile, response.data.percentileBits)
})
break
}
}
this.reloadMinutelyBandwidthChart = function () {
let axis = teaweb.bitsAxis(this.bandwidthStats, function (stat) {
return stat.bits
})
teaweb.renderLineChart({
id: "bandwidth-chart",
values: this.bandwidthStats,
axis: axis,
x: function (stat) {
return stat.timeAt
},
value: function (stat) {
return stat.bits / axis.divider
},
tooltip: function (args, values) {
if (args.componentType == "markLine") {
return args.name
}
let index = args.dataIndex
let day = values[index].day
return day.substr(0, 4) + "-" + day.substr(4, 2) + "-" + day.substr(6, 2) + " " + values[index].timeAt + "<br/>峰值带宽:" + teaweb.formatBits(values[index].bits)
},
markLine: {
precision: 4,
data: [{
name: this.bandwidthPercentile + "th",
yAxis: this.bandwidthPercentileBits/axis.divider
}],
symbol: "none",
lineStyle: {
color: teaweb.chartColor("red")
}
},
left: 30,
right: 40
})
}
this.reloadHourlyBandwidthChart = function (percentile, percentileBits) {
let axis = teaweb.bitsAxis(this.hourlyBandwidthStats, function (stat) {
return stat.bits
})
teaweb.renderLineChart({
id: "bandwidth-chart",
values: this.hourlyBandwidthStats,
axis: axis,
x: function (stat) {
return stat.hour
},
value: function (stat) {
return stat.bits / axis.divider
},
tooltip: function (args, values) {
if (args.componentType == "markLine") {
return args.name
}
let index = args.dataIndex
let day = values[index].day
return day.substr(0, 4) + "-" + day.substr(4, 2) + "-" + day.substr(6, 2) + " " + values[index].hour + "时<br/>峰值带宽:" + teaweb.formatBits(values[index].bits)
},
markLine: {
precision: 4,
data: [{
name: percentile + "th",
yAxis: percentileBits/axis.divider
}],
symbol: "none",
lineStyle: {
color: teaweb.chartColor("red")
}
},
left: 30,
right: 40
})
}
this.reloadDailyBandwidthChart = function (percentile, percentileBits) {
let axis = teaweb.bitsAxis(this.dailyBandwidthStats, function (stat) {
return stat.bits
})
teaweb.renderLineChart({
id: "bandwidth-chart",
values: this.dailyBandwidthStats,
axis: axis,
x: function (stat) {
let day = stat.day
return day.substr(4, 2) + "-" + day.substr(6, 2)
},
value: function (stat) {
return stat.bits / axis.divider
},
tooltip: function (args, values) {
if (args.componentType == "markLine") {
return args.name
}
let index = args.dataIndex
let day = values[index].day
return day.substr(0, 4) + "-" + day.substr(4, 2) + "-" + day.substr(6, 2) + "<br/>峰值带宽:" + teaweb.formatBits(values[index].bits)
},
markLine: {
precision: 4,
data: [{
name: percentile + "th",
yAxis: percentileBits/axis.divider
}],
symbol: "none",
lineStyle: {
color: teaweb.chartColor("red")
}
},
left: 30,
right: 40
})
}
this.selectTrafficTab = function (tab) {
this.trafficTab = tab
if (tab == "hourly") {
this.$delay(function () {
this.reloadHourlyTrafficChart()
})
} else if (tab == "daily") {
this.$delay(function () {
this.reloadDailyTrafficChart()
})
}
}
this.reloadHourlyTrafficChart = function () {
let stats = this.hourlyStats
this.reloadTrafficChart("hourly-traffic-chart", "流量统计", stats, function (args) {
let index = args.dataIndex
let cachedRatio = 0
let attackRatio = 0
if (stats[index].bytes > 0) {
cachedRatio = Math.round(stats[index].cachedBytes * 10000 / stats[index].bytes) / 100
attackRatio = Math.round(stats[index].attackBytes * 10000 / stats[index].bytes) / 100
}
return stats[index].day + " " + stats[index].hour + "时<br/>总流量:" + teaweb.formatBytes(stats[index].bytes) + "<br/>缓存流量:" + teaweb.formatBytes(stats[index].cachedBytes) + "<br/>缓存命中率:" + cachedRatio + "%<br/>拦截攻击流量:" + teaweb.formatBytes(stats[index].attackBytes) + "<br/>拦截比例:" + attackRatio + "%"
})
}
this.reloadDailyTrafficChart = function () {
let stats = this.dailyStats
this.reloadTrafficChart("daily-traffic-chart", "流量统计", stats, function (args) {
let index = args.dataIndex
let cachedRatio = 0
let attackRatio = 0
if (stats[index].bytes > 0) {
cachedRatio = Math.round(stats[index].cachedBytes * 10000 / stats[index].bytes) / 100
attackRatio = Math.round(stats[index].attackBytes * 10000 / stats[index].bytes) / 100
}
return stats[index].day + "<br/>总流量:" + teaweb.formatBytes(stats[index].bytes) + "<br/>缓存流量:" + teaweb.formatBytes(stats[index].cachedBytes) + "<br/>缓存命中率:" + cachedRatio + "%<br/>拦截攻击流量:" + teaweb.formatBytes(stats[index].attackBytes) + "<br/>拦截比例:" + attackRatio + "%"
})
}
this.reloadTrafficChart = function (chartId, name, stats, tooltipFunc) {
let chartBox = document.getElementById(chartId)
if (chartBox == null) {
return
}
let axis = teaweb.bytesAxis(stats, function (v) {
return Math.max(v.bytes, v.cachedBytes)
})
let chart = teaweb.initChart(chartBox)
let option = {
xAxis: {
data: stats.map(function (v) {
if (v.hour != null) {
return v.hour
}
return v.day
})
},
yAxis: {
axisLabel: {
formatter: function (value) {
return value + axis.unit
}
}
},
tooltip: {
show: true,
trigger: "item",
formatter: tooltipFunc
},
grid: {
left: 50,
top: 40,
right: 20,
bottom: 20
},
series: [
{
name: "流量",
type: "line",
data: stats.map(function (v) {
return v.bytes / axis.divider
}),
itemStyle: {
color: teaweb.DefaultChartColor
},
areaStyle: {
color: teaweb.DefaultChartColor
},
smooth: true
},
{
name: "缓存流量",
type: "line",
data: stats.map(function (v) {
return v.cachedBytes / axis.divider
}),
itemStyle: {
color: "#61A0A8"
},
areaStyle: {
color: "#61A0A8"
},
smooth: true
},
{
name: "攻击流量",
type: "line",
data: stats.map(function (v) {
return v.attackBytes / axis.divider;
}),
itemStyle: {
color: "#F39494"
},
areaStyle: {
color: "#F39494"
},
smooth: true
}
],
legend: {
data: ["流量", "缓存流量", "攻击流量"]
},
animation: true
}
chart.setOption(option)
chart.resize()
}
/**
* 请求数统计
*/
this.requestsTab = "hourly"
this.selectRequestsTab = function (tab) {
this.requestsTab = tab
if (tab == "hourly") {
this.$delay(function () {
this.reloadHourlyRequestsChart()
})
} else if (tab == "daily") {
this.$delay(function () {
this.reloadDailyRequestsChart()
})
}
}
this.reloadHourlyRequestsChart = function () {
let stats = this.hourlyStats
this.reloadRequestsChart("hourly-requests-chart", "请求数统计", stats, function (args) {
let index = args.dataIndex
let cachedRatio = 0
let attackRatio = 0
if (stats[index].countRequests > 0) {
cachedRatio = Math.round(stats[index].countCachedRequests * 10000 / stats[index].countRequests) / 100
attackRatio = Math.round(stats[index].countAttackRequests * 10000 / stats[index].countRequests) / 100
}
return stats[index].day + " " + stats[index].hour + "时<br/>总请求数:" + teaweb.formatNumber(stats[index].countRequests) + "<br/>缓存请求数:" + teaweb.formatNumber(stats[index].countCachedRequests) + "<br/>缓存命中率:" + cachedRatio + "%<br/>拦截攻击数:" + teaweb.formatNumber(stats[index].countAttackRequests) + "<br/>拦截比例:" + attackRatio + "%"
})
}
this.reloadDailyRequestsChart = function () {
let stats = this.dailyStats
this.reloadRequestsChart("daily-requests-chart", "请求数统计", stats, function (args) {
let index = args.dataIndex
let cachedRatio = 0
let attackRatio = 0
if (stats[index].countRequests > 0) {
cachedRatio = Math.round(stats[index].countCachedRequests * 10000 / stats[index].countRequests) / 100
attackRatio = Math.round(stats[index].countAttackRequests * 10000 / stats[index].countRequests) / 100
}
return stats[index].day + "<br/>总请求数:" + teaweb.formatNumber(stats[index].countRequests) + "<br/>缓存请求数:" + teaweb.formatNumber(stats[index].countCachedRequests) + "<br/>缓存命中率:" + cachedRatio + "%<br/>拦截攻击数:" + teaweb.formatNumber(stats[index].countAttackRequests) + "<br/>拦截比例:" + attackRatio + "%"
})
}
this.reloadRequestsChart = function (chartId, name, stats, tooltipFunc) {
let chartBox = document.getElementById(chartId)
if (chartBox == null) {
return
}
let axis = teaweb.countAxis(stats, function (v) {
return Math.max(v.countRequests, v.countCachedRequests)
})
let chart = teaweb.initChart(chartBox)
let option = {
xAxis: {
data: stats.map(function (v) {
if (v.hour != null) {
return v.hour
}
if (v.day != null) {
return v.day
}
return ""
})
},
yAxis: {
axisLabel: {
formatter: function (value) {
return value + axis.unit
}
}
},
tooltip: {
show: true,
trigger: "item",
formatter: tooltipFunc
},
grid: {
left: 50,
top: 40,
right: 20,
bottom: 20
},
series: [
{
name: "请求数",
type: "line",
data: stats.map(function (v) {
return v.countRequests / axis.divider
}),
itemStyle: {
color: teaweb.DefaultChartColor
},
areaStyle: {
color: teaweb.DefaultChartColor
},
smooth: true
},
{
name: "缓存请求数",
type: "line",
data: stats.map(function (v) {
return v.countCachedRequests / axis.divider
}),
itemStyle: {
color: "#61A0A8"
},
areaStyle: {
color: "#61A0A8"
},
smooth: true
},
{
name: "攻击请求数",
type: "line",
data: stats.map(function (v) {
return v.countAttackRequests / axis.divider;
}),
itemStyle: {
color: "#F39494"
},
areaStyle: {
color: "#F39494"
},
smooth: true
}
],
legend: {
data: ["请求数", "缓存请求数", "攻击请求数"]
},
animation: true
}
chart.setOption(option)
chart.resize()
}
// 域名排行
this.reloadTopDomainsChart = function () {
let axis = teaweb.countAxis(this.topDomainStats, function (v) {
return v.countRequests
})
teaweb.renderBarChart({
id: "top-domains-chart",
name: "域名",
values: this.topDomainStats,
x: function (v) {
return v.domain
},
tooltip: function (args, stats) {
return stats[args.dataIndex].domain + "<br/>请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "<br/>流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes)
},
value: function (v) {
return v.countRequests / axis.divider;
},
axis: axis
})
}
})

View File

@@ -0,0 +1,25 @@
.chart-box {
height: 14em;
}
.traffic-map-box {
height: 16em;
div::-webkit-scrollbar {
width: 4px;
}
}
h4 {
span {
font-size: 0.8em;
color: grey;
font-weight: normal !important;
}
}
.menu {
.item span.small {
font-size: 0.8em;
}
}

View File

@@ -0,0 +1,7 @@
.delete-box {
margin-top: 1em;
}
.delete-box button {
width: 20em!important;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,eAAA;;AADD,WAGC;EACC,qBAAA","file":"index.css"}

View File

@@ -0,0 +1,15 @@
{$layout}
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server/delete?serverId=' + server.id" active="true">"{{server.name}}"删除</menu-item>
<span class="disabled item">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/delete?serverId=${serverId}'"></more-items-angle>
</first-menu>
<div class="delete-box">
<button class="ui button red large fluid" type="button" @click.prevent="deleteServer(serverId)">删除当前网站</button>
</div>

View File

@@ -0,0 +1,15 @@
Tea.context(function () {
this.deleteServer = function (serverId) {
teaweb.confirm("html:确定要删除当前网站吗?<br/>请慎重操作,删除后无法恢复!", function () {
this.$post("$")
.params({
"serverId": serverId
})
.success(function () {
teaweb.success("删除成功", function () {
window.location = "/servers"
})
})
})
}
})

View File

@@ -0,0 +1,7 @@
.delete-box {
margin-top: 1em;
button {
width: 20em!important;
}
}

View File

@@ -0,0 +1 @@
undefined

View File

@@ -0,0 +1,5 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
</div>

View File

@@ -0,0 +1,72 @@
{$template "/datepicker"}
{$layout}
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server/log?serverId=' + server.id" active="true">"{{server.name}}"日志</menu-item>
<span class="disabled item">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/log?serverId=${serverId}'"></more-items-angle>
</first-menu>
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<!-- 集群设置提醒 -->
<div v-if="!clusterAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前集群已经设置不允许网站记录访问日志,可以在"集群设置" -- "网站设置"中修改此选项。</warning-message>
</div>
<!-- 网站设置提醒 -->
<div v-if="!serverAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前网站尚未启用访问日志,可以在 <a :href="'/servers/server/settings/accessLog?serverId=' + serverId">[这里]</a> 修改。</warning-message>
</div>
<first-menu>
<menu-item :href="buildLogUrl('')" :active="hasError == 0 && hasWAF == 0">所有日志</menu-item>
<menu-item :href="buildLogUrl('hasError=1')" :active="hasError > 0">错误日志</menu-item>
<menu-item :href="buildLogUrl('hasWAF=1')" :active="hasWAF > 0">WAF日志</menu-item>
</first-menu>
<form method="get" class="ui form small" :action="path">
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="hasError" :value="hasError"/>
<input type="hidden" name="hasWAF" :value="hasWAF"/>
<http-access-log-search-box :v-ip="ip" :v-domain="domain" :v-keyword="keyword" :v-cluster-id="clusterId" :v-node-id="nodeId">
<div class="ui field">
<input type="text" name="day" maxlength="10" placeholder="选择日期" style="width:7.8em" id="day-input" v-model="day"/>
</div>
<div class="ui field">
<div class="ui right labeled input">
<input type="text" name="hour" maxlength="2" style="width: 3.5em" v-model="hour"/>
<span class="ui label"></span>
</div>
</div>
</http-access-log-search-box>
</form>
<!-- 分区 -->
<http-access-log-partitions-box :v-day="day" :v-partition="partition" :v-query="currentQuery"></http-access-log-partitions-box>
<p class="comment" v-if="accessLogs.length == 0">暂时还没有访问日志。</p>
<table class="ui table selectable" v-if="accessLogs.length > 0">
<!-- 这里之所以需要添加 :key是因为要不然不会刷新显示 -->
<tr v-for="accessLog in accessLogs" :key="accessLog.requestId">
<td><http-access-log-box :v-access-log="accessLog" :v-keyword="keyword"></http-access-log-box></td>
</tr>
</table>
<div v-if="accessLogs.length > 0">
<a :href="buildLogUrl('requestId=' + lastRequestId + '&hasError=' + hasError + '&hasWAF=' + hasWAF + '&partition=' + partition)" v-if="hasPrev">上一页</a>
<span v-else class="disabled">上一页</span>
<span class="disabled">&nbsp; | &nbsp;</span>
<a :href="buildLogUrl('requestId=' + nextRequestId + '&hasError=' + hasError + '&hasWAF=' + hasWAF + '&partition=' + partition)" v-if="hasMore">下一页</a>
<span v-else class="disabled">下一页</span>
<page-size-selector></page-size-selector>
</div>
</div>

View File

@@ -0,0 +1,82 @@
Tea.context(function () {
this.$delay(function () {
let that = this
teaweb.datepicker("day-input", function (day) {
that.day = day
})
})
let that = this
this.accessLogs.forEach(function (accessLog) {
if (typeof (that.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = that.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
if (accessLog.firewallRuleSetId > 0 && typeof (that.wafInfos[accessLog.firewallRuleSetId]) == "object") {
accessLog.wafInfo = that.wafInfos[accessLog.firewallRuleSetId]
} else {
accessLog.wafInfo = null
}
})
this.query = function (args) {
// 初始化时页面尚未设置Vue变量所以使用全局的变量获取
let that = TEA.ACTION.data
if (that.serverId == null) {
that.serverId = 0
}
if (that.keyword == null) {
that.keyword = ""
}
if (that.ip == null) {
that.ip = ""
}
if (that.domain == null) {
that.domain = ""
}
if (that.pageSize == null) {
that.pageSize = ""
}
if (that.day == null) {
that.day = ""
}
if (that.hour == null) {
that.hour = ""
}
let query = 'serverId=' + that.serverId + '&day=' + that.day + '&keyword=' + encodeURIComponent(that.keyword) + '&ip=' + that.ip + '&domain=' + that.domain + '&hour=' + that.hour + '&pageSize=' + that.pageSize
if (args != null && args.length > 0) {
query += "&" + args
}
return query
}
this.allQuery = function () {
if (this.query == null) {
// 尚未初始化完成
return
}
let query = this.query()
if (this.hasError == 1) {
query += "&hasError=1"
}
if (this.hasWAF == 1) {
query += "&hasWAF=1"
}
return query
}
this.currentQuery = this.allQuery()
// 构建日志URL
this.buildLogUrl = function (args) {
// 使用全局数据,确保在模板中也能正确访问
let that = TEA.ACTION.data
let query = this.query()
if (args != null && args.length > 0) {
query += "&" + args
}
return (that.path || this.path || "") + "?" + query
}
})

View File

@@ -0,0 +1,40 @@
{$layout}
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server/log?serverId=' + server.id" active="true">"{{server.name}}"日志</menu-item>
<span class="disabled item">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/log?serverId=${serverId}'"></more-items-angle>
</first-menu>
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<!-- 集群设置提醒 -->
<div v-if="!clusterAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前集群已经设置不允许网站记录访问日志,可以在"集群设置" -- "网站设置"中修改此选项。</warning-message>
</div>
<!-- 网站设置提醒 -->
<div v-if="!serverAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前网站尚未启用访问日志,可以在 <a :href="'/servers/server/settings/accessLog?serverId=' + serverId">[这里]</a> 修改。</warning-message>
</div>
<form method="get" class="ui form small" :action="path" autocomplete="off">
<input type="hidden" name="serverId" :value="serverId"/>
<http-access-log-search-box :v-ip="ip" :v-domain="domain" :v-keyword="keyword" :v-cluster-id="clusterId" :v-node-id="nodeId"></http-access-log-search-box>
</form>
<p class="comment" v-if="isLoaded && accessLogs.length == 0">今天暂时还没有访问日志。</p>
<table class="ui table selectable" v-if="accessLogs.length > 0">
<!-- 这里之所以需要添加 :key是因为要不然不会刷新显示 -->
<tr v-for="accessLog in accessLogs" :key="accessLog.requestId">
<td><http-access-log-box :v-access-log="accessLog" :v-keyword="keyword"></http-access-log-box></td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,82 @@
Tea.context(function () {
this.$delay(function () {
this.load()
})
this.hasMore = false
this.accessLogs = []
this.isLoaded = false
this.load = function () {
// 如果有弹窗时,暂时不更新
if (teaweb.hasPopup()) {
this.$delay(function () {
this.load()
}, 5000)
return
}
this.$post("$")
.params({
serverId: this.serverId,
requestId: this.requestId,
keyword: this.keyword,
ip: this.ip,
domain: this.domain,
clusterId: this.clusterId,
nodeId: this.nodeId
})
.success(function (resp) {
this.accessLogs = resp.data.accessLogs.concat(this.accessLogs)
// 添加区域信息
let that = this
this.accessLogs.forEach(function (accessLog) {
that.formatTime(accessLog)
if (typeof (resp.data.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = resp.data.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
if (accessLog.firewallRuleSetId > 0 && typeof (resp.data.wafInfos[accessLog.firewallRuleSetId]) == "object") {
accessLog.wafInfo = resp.data.wafInfos[accessLog.firewallRuleSetId]
} else {
accessLog.wafInfo = null
}
})
let max = 100
if (this.accessLogs.length > max) {
this.accessLogs = this.accessLogs.slice(0, max)
}
this.hasMore = resp.data.hasMore
this.requestId = resp.data.requestId
})
.done(function () {
if (!this.isLoaded) {
this.$delay(function () {
this.isLoaded = true
})
}
// 自动刷新
this.$delay(function () {
this.load()
}, 5000)
})
}
this.formatTime = function (accessLog) {
let elapsedSeconds = Math.ceil(new Date().getTime() / 1000) - accessLog.timestamp
if (elapsedSeconds >= 0) {
if (elapsedSeconds < 60) {
accessLog.humanTime = elapsedSeconds + "秒前"
} else if (elapsedSeconds < 3600) {
accessLog.humanTime = Math.ceil(elapsedSeconds / 60) + "分钟前"
} else if (elapsedSeconds < 3600 * 24) {
accessLog.humanTime = Math.ceil(elapsedSeconds / 3600) + "小时前"
}
}
}
})

View File

@@ -0,0 +1,60 @@
{$layout}
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server/log?serverId=' + server.id" active="true">"{{server.name}}"日志</menu-item>
<span class="disabled item">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/log?serverId=${serverId}'"></more-items-angle>
</first-menu>
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<!-- 集群设置提醒 -->
<div v-if="!clusterAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前集群已经设置不允许网站记录访问日志,可以在"集群设置" -- "网站设置"中修改此选项。</warning-message>
</div>
<!-- 网站设置提醒 -->
<div v-if="!serverAccessLogIsOn">
<div class="margin"></div>
<warning-message>当前网站尚未启用访问日志,可以在 <a :href="'/servers/server/settings/accessLog?serverId=' + serverId">[这里]</a> 修改。</warning-message>
</div>
<first-menu>
<menu-item :href="buildLogUrl('')" :active="hasError == 0 && hasWAF == 0">所有日志</menu-item>
<menu-item :href="buildLogUrl('hasError=1')" :active="hasError > 0">错误日志</menu-item>
<menu-item :href="buildLogUrl('hasWAF=1')" :active="hasWAF > 0">WAF日志</menu-item>
</first-menu>
<form method="get" class="ui form small" :action="path">
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="hasError" :value="hasError"/>
<input type="hidden" name="hasWAF" :value="hasWAF"/>
<http-access-log-search-box :v-ip="ip" :v-domain="domain" :v-keyword="keyword" :v-cluster-id="clusterId" :v-node-id="nodeId"></http-access-log-search-box>
</form>
<!-- 分区 -->
<http-access-log-partitions-box :v-day="day" :v-partition="partition" :v-query="currentQuery"></http-access-log-partitions-box>
<p class="comment" v-if="accessLogs.length == 0">今天暂时还没有访问日志。</p>
<table class="ui table selectable" v-if="accessLogs.length > 0">
<!-- 这里之所以需要添加 :key是因为要不然不会刷新显示 -->
<tr v-for="accessLog in accessLogs" :key="accessLog.requestId">
<td><http-access-log-box :v-access-log="accessLog" :v-keyword="keyword"></http-access-log-box></td>
</tr>
</table>
<div v-if="accessLogs.length > 0">
<a :href="buildLogUrl('requestId=' + lastRequestId + '&hasError=' + hasError + '&hasWAF=' + hasWAF + '&partition=' + partition)" v-if="hasPrev">上一页</a>
<span v-else class="disabled">上一页</span>
<span class="disabled">&nbsp; | &nbsp;</span>
<a :href="buildLogUrl('requestId=' + nextRequestId + '&hasError=' + hasError + '&hasWAF=' + hasWAF + '&partition=' + partition)" v-if="hasMore">下一页</a>
<span v-else class="disabled">下一页</span>
<page-size-selector></page-size-selector>
</div>
</div>

View File

@@ -0,0 +1,69 @@
Tea.context(function () {
let that = this
this.accessLogs.forEach(function (accessLog) {
if (typeof (that.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = that.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
if (accessLog.firewallRuleSetId > 0 && typeof (that.wafInfos[accessLog.firewallRuleSetId]) == "object") {
accessLog.wafInfo = that.wafInfos[accessLog.firewallRuleSetId]
} else {
accessLog.wafInfo = null
}
})
this.query = function (args) {
// 初始化时页面尚未设置Vue变量所以使用全局的变量获取
let that = TEA.ACTION.data
if (that.serverId == null) {
that.serverId = 0
}
if (that.keyword == null) {
that.keyword = ""
}
if (that.ip == null) {
that.ip = ""
}
if (that.domain == null) {
that.domain = ""
}
if (that.pageSize == null) {
that.pageSize = ""
}
let query = 'serverId=' + that.serverId + '&keyword=' + encodeURIComponent(that.keyword) + '&ip=' + that.ip + '&domain=' + that.domain + '&pageSize=' + that.pageSize
if (args != null && args.length > 0) {
query += "&" + args
}
return query
}
this.allQuery = function () {
if (this.query == null) {
// 尚未初始化完成
return
}
let query = this.query()
if (this.hasError == 1) {
query += "&hasError=1"
}
if (this.hasWAF == 1) {
query += "&hasWAF=1"
}
return query
}
this.currentQuery = this.allQuery()
// 构建日志URL
this.buildLogUrl = function (args) {
// 使用全局数据,确保在模板中也能正确访问
let that = TEA.ACTION.data
let query = this.query()
if (args != null && args.length > 0) {
query += "&" + args
}
return (that.path || this.path || "") + "?" + query
}
})

View File

@@ -0,0 +1,7 @@
table td {
word-break: break-all;
}
table td.title {
width: 12em !important;
}
/*# sourceMappingURL=viewPopup.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["viewPopup.less"],"names":[],"mappings":"AAAA,KAAM;EACL,qBAAA;;AAGD,KAAM,GAAE;EACP,sBAAA","file":"viewPopup.css"}

View File

@@ -0,0 +1,160 @@
{$layout "layout_popup"}
{$template "/code_editor"}
<div class="ui menu tabular tiny">
<a class="item" :class="{active: tab == 'summary'}" @click.prevent="switchTab('summary')">综合信息</a>
<a class="item" :class="{active: tab == 'response'}" @click.prevent="switchTab('response')">响应数据Response</a>
<a class="item" :class="{active: tab == 'request'}" @click.prevent="switchTab('request')">请求数据Request</a>
<a class="item" :class="{active: tab == 'cookie'}" @click.prevent="switchTab('cookie')">Cookie</a>
<a class="item" :class="{active: tab == 'client'}" @click.prevent="switchTab('client')">终端信息</a>
</div>
<div v-if="tab == 'summary'">
<table class="ui table selectable small">
<tr>
<td style="width: 50%">请求概要<em>(Request)</em>{{accessLog.request}}</td>
<td>请求ID{{accessLog.requestId}}</td>
</tr>
<tr>
<td>请求方法<em>(RequestMethod)</em>{{accessLog.requestMethod}}</td>
<td>请求URI<em>(RequestURI)</em>{{accessLog.requestURI}}</td>
</tr>
<tr>
<td>主机地址<em>(Host)</em>{{accessLog.host}}</td>
<td>终端地址<em>(RemoteAddr:RemotePort)</em>{{accessLog.remoteAddr}}:{{accessLog.remotePort}}</td>
</tr>
<tr>
<td>请求来源<em>(Referer)</em>
<span v-if="accessLog.referer != null && accessLog.referer.length > 0">{{accessLog.referer}}</span>
<span v-else class="disabled">[没有设置]</span>
</td>
<td>终端信息<em>(UserAgent)</em>
<span v-if="accessLog.userAgent != null && accessLog.userAgent.length > 0">{{accessLog.userAgent}}</span>
<span v-else class="disabled">[没有设置]</span>
</td>
</tr>
<tr>
<td>状态<em>(StatusMessage)</em><span :class="{red:accessLog.status>=400, green:accessLog.status<400}">{{accessLog.status}} {{accessLog.statusMessage}} <span v-if="accessLog.originStatus >= 400 || (accessLog.originStatus > 0 && accessLog.originStatus != accessLog.status)" class="grey small">(源站{{accessLog.originStatus}}</span></span></td>
<td>文件类型<em>(ContentType)</em>
<span v-if="accessLog.contentType != null && accessLog.contentType.length > 0">{{accessLog.contentType}}</span>
<span v-else>-</span>
</td>
</tr>
<tr>
<td>下行流量<em>(BytesSent)</em>
<span v-if="accessLog.bytesSent != null">{{accessLog.bytesSent}}
<span v-if="accessLog.bytesSent > 0" class="grey small">{{teaweb.formatBytes(accessLog.bytesSent)}}</span>
</span>
<span v-else class="disabled">0</span>
</td>
<td>源站地址:
<span v-if="accessLog.originAddress != null && accessLog.originAddress">{{accessLog.originAddress}}</span>
<span v-else class="disabled">未访问源站</span>
</td>
</tr>
<tr>
<td>ISO8601时间{{accessLog.timeISO8601}}</td>
<td>本地时间<em>(TimeLocal)</em>{{accessLog.timeLocal}}</td>
</tr>
<tr v-if="wafInfo != null">
<td class="color-border">WAF策略{{wafInfo.policy.name}}</td>
<td>WAF规则分组
<span v-if="wafInfo.group != null">{{wafInfo.group.name}}</span>
<span v-else>-</span>
</td>
</tr>
<tr v-if="wafInfo != null && wafInfo.set != null">
<td class="color-border">WAF规则集
<a :href="'/servers/components/waf/group?firewallPolicyId=' + accessLog.firewallPolicyId + '&type=inbound&groupId=' + accessLog.firewallRuleGroupId+ '#set' + accessLog.firewallRuleSetId" v-if="wafInfo.policy.serverId == 0" target="_parent">{{wafInfo.set.name}}</a>
<a :href="'/servers/server/settings/waf/group?serverId=' + accessLog.serverId + '&firewallPolicyId=' + accessLog.firewallPolicyId + '&type=inbound&groupId=' + accessLog.firewallRuleGroupId + '#set' + accessLog.firewallRuleSetId" target="_parent" v-if="wafInfo.policy.serverId > 0">{{wafInfo.set.name}}</a> </td>
<td></td>
</tr>
<tr v-if="accessLog.errors != null && accessLog.errors.length > 0">
<td colspan="2">
<div v-for="error in accessLog.errors">
<pre><span class="red">{{error}}</span></pre>
</div>
</td>
</tr>
</table>
</div>
<div v-if="tab == 'response'">
<table class="ui table definition selectable small">
<tr>
<td class="title">Status</td>
<td>{{accessLog.status}} {{accessLog.statusMessage}}</td>
</tr>
</table>
<table class="ui table definition selectable small" v-if="responseHeaders.length > 0">
<tbody v-for="header in responseHeaders">
<tr v-for="value in header.values">
<td class="title">
<span v-if="header.isGeneral">{{header.name}}</span>
<span style="font-style: italic" v-else>{{header.name}}</span>
</td>
<td>{{value}}</td>
</tr>
</tbody>
</table>
</div>
<div v-if="tab == 'request'">
<table class="ui table definition selectable small">
<tbody v-for="header in requestHeaders">
<tr v-for="value in header.values">
<td class="title">
<span v-if="header.isGeneral">{{header.name}}</span>
<span style="font-style: italic" v-else>{{header.name}}</span>
</td>
<td>{{value}}</td>
</tr>
</tbody>
<tbody v-if="requestBody.length > 0">
<tr>
<td colspan="2">请求内容:</td>
</tr>
<tr>
<td colspan="2">
<source-code-box :type="requestContentType" width="0" height="200">{{requestBody}}</source-code-box>
</td>
</tr>
</tbody>
</table>
</div>
<div v-if="tab == 'cookie'">
<p class="comment" v-if="cookies.length == 0">暂时没有Cookie数据。</p>
<div v-else>
<table class="ui table definition selectable small">
<tr v-for="cookie in cookies">
<td class="title">{{cookie.name}}</td>
<td>{{cookie.value}}</td>
</tr>
</table>
</div>
</div>
<div v-if="tab == 'client'">
<table class="ui table definition selectable small">
<tr>
<td class="title">综合信息<em>(UserAgent)</em></td>
<td>
<span v-if="accessLog.userAgent != null && accessLog.userAgent.length > 0">{{accessLog.userAgent}}</span>
<span class="disabled" v-else>[没有设置]</span>
</td>
</tr>
<tr>
<td>IP</td>
<td>{{accessLog.remoteAddr}}</td>
</tr>
<tr v-if="region != null">
<td>区域</td>
<td>{{region.full}}</td>
</tr>
<tr v-if="region != null && region.isp != null && region.isp.length > 0">
<td>ISP</td>
<td>{{region.isp}}</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,74 @@
Tea.context(function () {
this.tab = "summary"
this.teaweb= teaweb
this.switchTab = function (tab) {
this.tab = tab
}
// 请求Header
this.requestHeaders = []
if (this.accessLog.header != null) {
for (let k in this.accessLog.header) {
let v = this.accessLog.header[k]
if (typeof (v) != "object") {
continue
}
this.requestHeaders.push({
name: k,
values: v.values,
isGeneral: !k.startsWith("X-")
})
}
}
this.requestHeaders.sort(function (v1, v2) {
return (v1.name < v2.name) ? -1 : 1
})
// 响应Header
this.responseHeaders = []
if (this.accessLog.sentHeader != null) {
for (let k in this.accessLog.sentHeader) {
let v = this.accessLog.sentHeader[k]
if (typeof (v) != "object") {
continue
}
this.responseHeaders.push({
name: k,
values: v.values,
isGeneral: !k.startsWith("X-")
})
}
}
this.responseHeaders.sort(function (v1, v2) {
return (v1.name < v2.name) ? -1 : 1
})
// Cookie
this.cookies = []
if (this.accessLog.cookie != null) {
for (let k in this.accessLog.cookie) {
let v = this.accessLog.cookie[k]
if (typeof (v) != "string") {
continue
}
this.cookies.push({
name: k,
value: v
})
}
}
this.cookies.sort(function (v1, v2) {
if (v1.name.startsWith("_")) {
if (v2.name.startsWith("_")) {
return (v1.name < v2.name) ? -1 : 1
}
return -1
}
if (v2.name.startsWith("_")) {
return 1
}
return (v1.name.toUpperCase() < v2.name.toUpperCase()) ? -1 : 1
})
})

View File

@@ -0,0 +1,7 @@
table td {
word-break: break-all;
}
table td.title {
width: 12em !important;
}

View File

@@ -0,0 +1,13 @@
<first-menu>
<menu-item href="/servers">网站列表</menu-item>
<span class="item disabled">|</span>
<menu-item :href="'/servers/server/settings?serverId=' + server.id" :active="leftMenuActiveItem == null">"{{server.name}}"设置</menu-item>
<span class="disabled item">&raquo;</span>
<a class="item active" v-if="leftMenuActiveItem != null" :href="leftMenuActiveItem.url">"{{leftMenuActiveItem.name}}"设置</a>
<span class="disabled item" v-if="leftMenuActiveItem != null">|</span>
<more-items-angle
:v-data-url="'/servers/nearby?serverId=' + server.id"
:v-url="'/servers/server/settings?serverId=${serverId}'"></more-items-angle>
<span class="disabled item" style="font-size: 0.8em" v-if="leftMenuActiveItem != null && leftMenuActiveItem.configCode != null && leftMenuActiveItem.configCode.length > 0 && teaIsPlus">|</span>
<server-config-copy-link :v-server-id="server.id" :v-config-code="leftMenuActiveItem.configCode" v-if="leftMenuActiveItem != null && leftMenuActiveItem.configCode != null && leftMenuActiveItem.configCode.length > 0 && teaIsPlus"></server-config-copy-link>
</first-menu>

View File

@@ -0,0 +1,203 @@
{$layout "layout_popup"}
<h3>创建鉴权方式</h3>
<form 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" name="name" maxlength="50" ref="focus"/>
</td>
</tr>
<tr>
<td>鉴权类型 *</td>
<td>
<select class="ui dropdown auto-width" name="type" v-model="type" @change="changeType">
<option value="">[鉴权类型]</option>
<option v-for="authType in authTypes" :value="authType.code">{{authType.name}}</option>
</select>
<p class="comment" v-html="authDescription"></p>
</td>
</tr>
<!-- TypeA -->
<tbody v-show="type == 'typeA'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeASecret" v-model="typeASecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeASecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>签名参数 *</td>
<td>
<input type="text" maxlength="30" name="typeASignParamName" value="sign" v-model="typeASignParamName" @input="changeTypeASignParamName"/>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeALife" value="30" style="width: 7em"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeB -->
<tbody v-show="type == 'typeB'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeBSecret" v-model="typeBSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeBSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeBLife" value="30" style="width: 7em"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeC -->
<tbody v-show="type == 'typeC'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeCSecret" v-model="typeCSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeCSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeCLife" value="30" style="width: 7em"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeD -->
<tbody v-show="type == 'typeD'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeDSecret" v-model="typeDSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeDSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>签名参数 *</td>
<td>
<input type="text" maxlength="30" name="typeDSignParamName" value="sign" v-model="typeDSignParamName" @input="changeTypeDSignParamName"/>
</td>
</tr>
<tr>
<td>时间戳参数 *</td>
<td>
<input type="text" maxlength="30" name="typeDTimestampParamName" value="t" v-model="typeDTimestampParamName" @input="changeTypeDTimestampParamName"/>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeDLife" value="30" style="width: 7em"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- BasicAuth -->
<tbody v-show="type == 'basicAuth'">
<tr>
<td>用户 *</td>
<td>
<http-auth-basic-auth-user-box></http-auth-basic-auth-user-box>
</td>
</tr>
<tr>
<td colspan="2">
<a href="" @click.prevent="showMoreBasicAuthOptions()">更多基本认证选项<i class="ui icon angle" :class="{up: moreBasicAuthOptionsVisible, down: !moreBasicAuthOptionsVisible}"></i></a>
</td>
</tr>
<tr v-show="moreBasicAuthOptionsVisible">
<td>认证领域名<em>Realm</em></td>
<td>
<input type="text" name="basicAuthRealm" value="" maxlength="100"/>
</td>
</tr>
<tr v-show="moreBasicAuthOptionsVisible">
<td>字符集</td>
<td>
<input type="text" name="basicAuthCharset" style="width: 6em" maxlength="50"/>
<p class="comment">类似于<code-label>UTF-8</code-label></p>
</td>
</tr>
</tbody>
<!-- SubRequest -->
<tbody v-show="type == 'subRequest'">
<tr>
<td>子请求URL *</td>
<td>
<input type="text" name="subRequestURL" maxlength="1024"/>
<p class="comment">可以是一个完整的URL也可以是一个路径。</p>
</td>
</tr>
<tr>
<td>请求方法</td>
<td>
<radio name="subRequestFollowRequest" :v-value="1" v-model="subRequestFollowRequest">同当前请求一致</radio> &nbsp; &nbsp;
<radio name="subRequestFollowRequest" :v-value="0" v-model="subRequestFollowRequest">自定义</radio>
<div style="margin-top: 0.8em" v-show="subRequestFollowRequest == 0">
<div class="ui divider"></div>
<select class="ui dropdown auto-width" name="subRequestMethod">
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="HEAD">HEAD</option>
</select>
</div>
</td>
</tr>
</tbody>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>限定文件扩展名</td>
<td>
<values-box name="exts"></values-box>
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号(.),比如<code-label>.png</code-label></p>
</td>
</tr>
<tr>
<td>限定域名</td>
<td>
<domains-box></domains-box>
<p class="comment">如果不为空,则表示只有这些域名的文件才需要鉴权。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,100 @@
Tea.context(function () {
this.success = NotifyPopup
this.type = ""
this.authDescription = ""
this.rawDescription = ""
this.changeType = function () {
let that = this
let authType = this.authTypes.$find(function (k, v) {
return v.code == that.type
})
if (authType != null) {
this.authDescription = authType.description
this.rawDescription = authType.description
} else {
this.authDescription = ""
this.rawDescription = ""
}
}
/**
* TypeA
*/
this.typeASecret = ""
this.typeASignParamName = "sign"
this.generateTypeASecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeASecret = resp.data.random
})
}
this.changeTypeASignParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeASignParamName + "=")
}
/**
* TypeB
*/
this.typeBSecret = ""
this.generateTypeBSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeBSecret = resp.data.random
})
}
/**
* TypeC
*/
this.typeCSecret = ""
this.generateTypeCSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeCSecret = resp.data.random
})
}
/**
* TypeD
*/
this.typeDSecret = ""
this.typeDSignParamName = "sign"
this.typeDTimestampParamName = "t"
this.generateTypeDSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeDSecret = resp.data.random
})
}
this.changeTypeDSignParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeDSignParamName + "=")
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
}
this.changeTypeDTimestampParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeDSignParamName + "=")
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
}
/**
* 基本认证
*/
this.moreBasicAuthOptionsVisible = false
this.showMoreBasicAuthOptions = function () {
this.moreBasicAuthOptionsVisible = !this.moreBasicAuthOptionsVisible
}
/**
* 子请求
*/
this.subRequestFollowRequest = 1
})

View File

@@ -0,0 +1,11 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form class="ui form" data-tea-action="$" data-tea-success="success" ref="authForm">
<input type="hidden" name="webId" :value="webId">
<http-auth-config-box :v-auth-config="authConfig" @change="changeMethods"></http-auth-config-box>
<submit-btn @ref=""></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,10 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.changeMethods = function (config) {
Tea.action("$")
.form(this.$refs.authForm)
.post()
teaweb.successRefresh("保存成功")
}
})

View File

@@ -0,0 +1,206 @@
{$layout "layout_popup"}
<h3>修改鉴权方式</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="policyId" :value="policy.id"/>
<table class="ui table definition selectable">
<tr>
<td class="title">名称 *</td>
<td>
<input type="text" name="name" maxlength="50" ref="focus" v-model="policy.name"/>
</td>
</tr>
<tr>
<td>鉴权类型 *</td>
<td>
{{policy.typeName}}
<p class="comment" v-html="authDescription"></p>
</td>
</tr>
<!-- TypeA -->
<tbody v-show="type == 'typeA'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeASecret" v-model="typeASecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeASecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>签名参数 *</td>
<td>
<input type="text" maxlength="30" name="typeASignParamName" value="sign" v-model="typeASignParamName" @input="changeTypeASignParamName"/>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeALife" value="30" style="width: 7em" v-model="policy.params.life"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeB -->
<tbody v-show="type == 'typeB'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeBSecret" v-model="typeBSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeBSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeBLife" value="30" style="width: 7em" v-model="policy.params.life"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeC -->
<tbody v-show="type == 'typeC'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeCSecret" v-model="typeCSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeCSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeCLife" value="30" style="width: 7em" v-model="policy.params.life"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- TypeD -->
<tbody v-show="type == 'typeD'">
<tr>
<td>鉴权密钥 *</td>
<td>
<input type="text" maxlength="40" name="typeDSecret" v-model="typeDSecret" autocomplete="off"/>
<p class="comment">只能包含字母、数字长度不超过40。<a href="" @click.prevent="generateTypeDSecret()">[随机生成]</a></p>
</td>
</tr>
<tr>
<td>签名参数 *</td>
<td>
<input type="text" maxlength="30" name="typeDSignParamName" value="sign" v-model="typeDSignParamName" @input="changeTypeDSignParamName"/>
</td>
</tr>
<tr>
<td>时间戳参数 *</td>
<td>
<input type="text" maxlength="30" name="typeDTimestampParamName" value="t" v-model="typeDTimestampParamName" @input="changeTypeDTimestampParamName"/>
</td>
</tr>
<tr>
<td>有效时间</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="8" name="typeDLife" value="30" style="width: 7em" v-model="policy.params.life"/>
<span class="ui label"></span>
</div>
<p class="comment">链接有效时间。</p>
</td>
</tr>
</tbody>
<!-- BasicAuth -->
<tbody v-show="type == 'basicAuth'">
<tr>
<td>用户 *</td>
<td>
<http-auth-basic-auth-user-box :v-users="policy.params.users"></http-auth-basic-auth-user-box>
</td>
</tr>
<tr>
<td colspan="2">
<a href="" @click.prevent="showMoreBasicAuthOptions()">更多基本认证选项<i class="ui icon angle" :class="{up: moreBasicAuthOptionsVisible, down: !moreBasicAuthOptionsVisible}"></i></a>
</td>
</tr>
<tr v-show="moreBasicAuthOptionsVisible">
<td>认证领域名<em>Realm</em></td>
<td>
<input type="text" name="basicAuthRealm" value="" maxlength="100" v-model="policy.params.realm"/>
</td>
</tr>
<tr v-show="moreBasicAuthOptionsVisible">
<td>字符集</td>
<td>
<input type="text" name="basicAuthCharset" style="width: 6em" v-model="policy.params.charset" maxlength="50"/>
<p class="comment">类似于<code-label>utf-8</code-label></p>
</td>
</tr>
</tbody>
<!-- SubRequest -->
<tbody v-show="type == 'subRequest'">
<tr>
<td>子请求URL *</td>
<td>
<input type="text" name="subRequestURL" maxlength="1024" v-model="policy.params.url"/>
<p class="comment">可以是一个完整的URL也可以是一个路径。</p>
</td>
</tr>
<tr>
<td>请求方法</td>
<td>
<radio name="subRequestFollowRequest" :v-value="1" v-model="subRequestFollowRequest">同当前请求一致</radio> &nbsp; &nbsp;
<radio name="subRequestFollowRequest" :v-value="0" v-model="subRequestFollowRequest">自定义</radio>
<div style="margin-top: 0.8em" v-show="subRequestFollowRequest == 0">
<div class="ui divider"></div>
<select class="ui dropdown auto-width" name="subRequestMethod" v-model="policy.params.method">
<option value="">[请选择]</option>
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="HEAD">HEAD</option>
</select>
</div>
</td>
</tr>
</tbody>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>限定文件扩展名</td>
<td>
<values-box name="exts" :v-values="policy.params.exts"></values-box>
<p class="comment">如果不为空,则表示只有这些扩展名的文件才需要鉴权;扩展名需要包含点符号(.),比如<code-label>.png</code-label></p>
</td>
</tr>
<tr>
<td>限定域名</td>
<td>
<domains-box :v-domains="policy.params.domains"></domains-box>
<p class="comment">如果不为空,则表示只有这些域名的文件才需要鉴权。</p>
</td>
</tr>
<tr>
<td>启用当前鉴权</td>
<td><checkbox name="isOn" value="1" v-model="policy.isOn"></checkbox></td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,131 @@
Tea.context(function () {
this.success = NotifyPopup
this.type = this.policy.type
this.authDescription = ""
this.rawDescription = ""
this.$delay(function () {
this.changeType()
})
this.changeType = function () {
let that = this
let authType = this.authTypes.$find(function (k, v) {
return v.code == that.type
})
if (authType != null) {
this.policy.typeName = authType.name
this.authDescription = authType.description
this.rawDescription = authType.description
} else {
this.authDescription = ""
this.rawDescription = ""
}
}
/**
* TypeA
*/
this.typeASecret = ""
this.typeASignParamName = "sign"
if (this.policy.type == "typeA") {
this.typeASecret = this.policy.params.secret
this.typeASignParamName = this.policy.params.signParamName
this.$delay(function () {
this.changeTypeASignParamName()
})
}
this.generateTypeASecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeASecret = resp.data.random
})
}
this.changeTypeASignParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeASignParamName + "=")
}
/**
* TypeB
*/
this.typeBSecret = ""
if (this.policy.type == "typeB") {
this.typeBSecret = this.policy.params.secret
}
this.generateTypeBSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeBSecret = resp.data.random
})
}
/**
* TypeC
*/
this.typeCSecret = ""
if (this.policy.type == "typeC") {
this.typeCSecret = this.policy.params.secret
}
this.generateTypeCSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeCSecret = resp.data.random
})
}
/**
* TypeD
*/
this.typeDSecret = ""
this.typeDSignParamName = "sign"
this.typeDTimestampParamName = "t"
if (this.policy.type == "typeD") {
this.typeDSecret = this.policy.params.secret
this.typeDSignParamName = this.policy.params.signParamName
this.typeDTimestampParamName = this.policy.params.timestampParamName
this.$delay(function () {
this.changeTypeDSignParamName()
this.changeTypeDTimestampParamName()
})
}
this.generateTypeDSecret = function () {
this.$post(".random")
.success(function (resp) {
this.typeDSecret = resp.data.random
})
}
this.changeTypeDSignParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeDSignParamName + "=")
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
}
this.changeTypeDTimestampParamName = function () {
this.authDescription = this.rawDescription.replace("sign=", this.typeDSignParamName + "=")
this.authDescription = this.authDescription.replace("t=", this.typeDTimestampParamName + "=")
}
/**
* 基本鉴权
*/
this.moreBasicAuthOptionsVisible = false
this.showMoreBasicAuthOptions = function () {
this.moreBasicAuthOptionsVisible = !this.moreBasicAuthOptionsVisible
}
/**
* 子请求
*/
this.subRequestFollowRequest = (this.policy.params.method != null && this.policy.params.method.length > 0) ? 0 : 1
})

View File

@@ -0,0 +1,21 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div v-if="hasGroupConfig">
<div class="margin"></div>
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
</div>
<div :class="{'opacity-mask': hasGroupConfig}">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<http-access-log-config-box
:v-access-log-config="accessLogConfig"
:v-fields="fields"
:v-default-field-codes="defaultFieldCodes"></http-access-log-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,5 @@
<first-menu>
<menu-item :href="'.?serverId=' + serverId" code="index">设置</menu-item>
<menu-item :href="'.purge?serverId=' + serverId" code="purge">刷新</menu-item>
<menu-item :href="'.fetch?serverId=' + serverId" code="fetch">预热</menu-item>
</first-menu>

View File

@@ -0,0 +1,13 @@
{$layout "layout_popup"}
<h3 v-if="!isReverse && cacheRef == null">添加缓存条件</h3>
<h3 v-if="!isReverse && cacheRef != null">修改缓存条件</h3>
<h3 v-if="isReverse && cacheRef == null">添加不缓存条件</h3>
<h3 v-if="isReverse && cacheRef != null">修改不缓存条件</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<table class="ui table definition selectable">
<tbody is="http-cache-ref-box" :v-cache-ref="cacheRef" :v-is-reverse="isReverse"></tbody>
</table>
<p class="comment" v-if="isReverse">如果请求满足当前添加的条件,则不缓存。</p>
<submit-btn>确定</submit-btn>
</form>

View File

@@ -0,0 +1,9 @@
Tea.context(function () {
this.success = NotifyPopup
this.cacheRef = null
if (window.parent.UPDATING_CACHE_REF != null) {
this.cacheRef = window.parent.UPDATING_CACHE_REF
this.isReverse = this.cacheRef.isReverse
}
})

View File

@@ -0,0 +1,41 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<div class="margin"></div>
<div v-show="webConfig.cache == null || !webConfig.cache.isOn">
<p class="comment">没有开启缓存,暂时无法预热缓存。</p>
</div>
<div v-show="webConfig.cache != null && webConfig.cache.isOn">
<p class="comment">可以在这里批量预热一组URL。</p>
<form method="post" class="ui form" data-tea-action="$" data-tea-before="before" data-tea-success="success" data-tea-fail="fail" data-tea-done="done" data-tea-timeout="3600">
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="webId" :value="webId"/>
<table class="ui table definition selectable">
<tr>
<td>URL列表</td>
<td>
<textarea name="keys" rows="10" ref="focus"></textarea>
<p class="comment">每行一个URL。</p>
</td>
</tr>
<tr>
<td class="title">操作结果</td>
<td>
<div v-if="isRequesting">数据发送中...</div>
<span class="red" v-if="!isRequesting && !isOk && message.length > 0">失败:{{message}}</span>
<div v-if="!isRequesting && !isOk && failKeys.length > 0" class="fail-keys-box">
<div v-for="failKey in failKeys">
<span class="red">{{failKey.key}}: {{failKey.reason}}</span>
</div>
</div>
</td>
</tr>
</table>
<submit-btn v-if="!isRequesting">提交</submit-btn>
</form>
</div>
</div>

View File

@@ -0,0 +1,32 @@
Tea.context(function () {
this.isRequesting = false
this.isOk = false
this.message = ""
this.failKeys = []
this.before = function () {
this.isRequesting = true
this.isOk = false
this.message = ""
this.failKeys = []
}
this.success = function (resp) {
this.isOk = true
let f = NotifyReloadSuccess("任务提交成功")
f()
}
this.fail = function (resp) {
this.message = resp.message
if (resp.data.failKeys != null) {
this.failKeys = resp.data.failKeys
}
}
this.done = function () {
this.isRequesting = false
}
});

View File

@@ -0,0 +1,21 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<div v-if="hasGroupConfig">
<div class="margin"></div>
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
</div>
<div :class="{'opacity-mask': hasGroupConfig}">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<http-cache-config-box :v-cache-config="cacheConfig" :v-cache-policy="cachePolicy" :v-web-id="webId"></http-cache-config-box>
<submit-btn></submit-btn>
<p class="comment">修改条件设置后请记得保存。</p>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,53 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
{$template "menu"}
<div class="margin"></div>
<div v-show="webConfig.cache == null || !webConfig.cache.isOn">
<p class="comment">没有开启缓存,不需要刷新。</p>
</div>
<div v-show="webConfig.cache != null && webConfig.cache.isOn">
<p class="comment">可以在这里批量刷新一组URL缓存。</p>
<form method="post" class="ui form" data-tea-action="$" data-tea-before="before" data-tea-success="success" data-tea-fail="fail" data-tea-done="done" data-tea-timeout="300">
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="webId" :value="webId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">URL类型</td>
<td>
<radio name="keyType" :v-value="'key'" v-model="keyType">URL</radio> &nbsp;
<radio name="keyType" :v-value="'prefix'" v-model="keyType">目录</radio>
</td>
</tr>
<tr>
<td>
<span v-if="keyType == 'key'">URL</span>
<span v-if="keyType == 'prefix'">目录</span>
</td>
<td>
<textarea name="keys" rows="10" ref="keysBox"></textarea>
<p class="comment" v-if="keyType == 'key'">每行一个URL比如<code-label>https://example.com/hello/world.html</code-label></p>
<p class="comment" v-if="keyType == 'prefix'">每行一个URL目录比如<code-label>https://example.com/hello/</code-label>;如果只填写域名部分,表示清理全站,比如<code-label>https://example.com/</code-label></p>
</td>
</tr>
<tr>
<td>操作结果</td>
<td>
<div v-if="isRequesting">数据发送中...</div>
<span class="red" v-if="!isRequesting && !isOk && message.length > 0">失败:{{message}}</span>
<div v-if="!isRequesting && !isOk && failKeys.length > 0" class="fail-keys-box">
<div v-for="failKey in failKeys">
<span class="red">{{failKey.key}}: {{failKey.reason}}</span>
</div>
</div>
</td>
</tr>
</table>
<submit-btn v-if="!isRequesting">提交</submit-btn>
</form>
</div>
</div>

View File

@@ -0,0 +1,43 @@
Tea.context(function () {
this.isRequesting = false
this.isOk = false
this.message = ""
this.failKeys = []
this.$delay(function () {
this.$refs.keysBox.focus()
this.$watch("keyType", function () {
this.$refs.keysBox.focus()
})
})
this.before = function () {
this.isRequesting = true
this.isOk = false
this.message = ""
this.failKeys = []
}
this.success = function () {
this.isOk = true
let f = NotifyReloadSuccess("任务提交成功")
f()
}
this.fail = function (resp) {
this.message = resp.message
if (resp.data.failKeys != null) {
this.failKeys = resp.data.failKeys
}
}
this.done = function () {
this.isRequesting = false
}
/**
* 操作类型
*/
this.keyType = "key" // key | prefix
})

View File

@@ -0,0 +1,14 @@
{$layout}
{$template "../settings_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="webId" :value="webId"/>
<csrf-token></csrf-token>
<http-cc-config-box :v-cc-config="ccConfig"></http-cc-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,18 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div v-if="hasGroupConfig">
<div class="margin"></div>
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
</div>
<div :class="{'opacity-mask': hasGroupConfig}">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<http-charsets-box :v-usual-charsets="usualCharsets" :v-all-charsets="allCharsets" :v-charset-config="charsetConfig"></http-charsets-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,16 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div class="margin"></div>
<div :class="{'opacity-mask': hasGroupConfig}">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="webId" :value="webId"/>
<http-common-config-box :v-common-config="commonConfig"></http-common-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,15 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<form method="post" class="ui form" data-tea-success="success" data-tea-action="$">
<input type="hidden" name="webId" :value="webId"/>
<input type="hidden" name="gzipId" :value="gzipConfig.id"/>
<http-gzip-box :v-gzip-ref="gzipRef" :v-gzip-config="gzipConfig"></http-gzip-box>
<div class="margin"></div>
<submit-btn></submit-btn>
</form>
</div>

View File

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

View File

@@ -0,0 +1,20 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div v-if="hasGroupConfig">
<div class="margin"></div>
<warning-message>由于已经在当前<a :href="groupSettingURL">网站分组</a>中进行了对应的配置,在这里的配置将不会生效。</warning-message>
</div>
<div :class="{'opacity-mask': hasGroupConfig}">
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="webId" :value="webId"/>
<http-compression-config-box :v-compression-config="compressionConfig"></http-compression-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,43 @@
{$layout "layout_popup"}
<h3 v-if="!isUpdating">添加子条件</h3>
<h3 v-if="isUpdating">修改子条件</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<table class="ui table definition selectable">
<tr>
<td class="title">选择条件类型</td>
<td>
<select class="ui dropdown auto-width" name="condType" v-model="condType" @change="changeCondType">
<option v-for="c in components" :value="c.type">{{c.name}}</option>
</select>
{$ range .components}
<p class="comment" v-if="condType == '{$ .Type}'">{$ .Description}</p>
{$ end}
</td>
</tr>
<tr v-if="condType != 'params'">
<td>{{paramsTitle}}</td>
<td>
{$ range .components}
{$if not (eq .Type "params") }
<{$ .Component} v-if="condType == '{$ .Type}'" :v-cond="cond" ref="component"></{$ .Component}>
{$end}
{$ end}
</td>
</tr>
<tr v-if="paramsCaseInsensitive">
<td>不区分大小写</td>
<td>
<div class="ui checkbox">
<input type="checkbox" v-model="isCaseInsensitive" @change="changeCaseInsensitive"/>
<label></label>
</div>
<p class="comment">选中后表示对比时忽略参数值的大小写。</p>
</td>
</tr>
<!-- 变量相关 -->
<tbody is="http-cond-params" :v-cond="cond" v-if="condType == 'params'"></tbody>
</table>
<submit-btn>确定</submit-btn>
</form>

View File

@@ -0,0 +1,55 @@
Tea.context(function () {
this.isUpdating = false
this.cond = null
this.paramsTitle = ""
this.paramsCaseInsensitive = false
this.isCaseInsensitive = false
this.success = NotifyPopup
this.condType = (this.components.length > 0) ? this.components[0].type : ""
// 是否正在修改
if (window.parent.UPDATING_COND != null) {
this.isUpdating = true
this.condType = window.parent.UPDATING_COND.type
this.cond = window.parent.UPDATING_COND
if (typeof (this.cond.isCaseInsensitive) == "boolean") {
this.isCaseInsensitive = this.cond.isCaseInsensitive
}
}
this.changeCondType = function () {
let that = this
let c = this.components.$find(function (k, v) {
return v.type == that.condType
})
if (c == null || c.paramsTitle.length == 0) {
that.paramsTitle = "条件参数"
that.paramsCaseInsensitive = false
} else {
that.paramsTitle = c.paramsTitle
if (typeof (c.caseInsensitive) != "undefined") {
that.paramsCaseInsensitive = c.caseInsensitive
that.$delay(function () {
that.changeCaseInsensitive()
})
} else {
that.paramsCaseInsensitive = false
}
}
}
this.$delay(function () {
this.changeCondType()
})
this.changeCaseInsensitive = function () {
let componentRef = this.$refs.component
if (componentRef == null) {
return
}
if (typeof (componentRef.changeCaseInsensitive) == "function") {
componentRef.changeCaseInsensitive(this.isCaseInsensitive)
}
}
})

View File

@@ -0,0 +1,59 @@
{$layout "layout_popup"}
<h3 v-if="!isUpdating">添加条件分组</h3>
<h3 v-if="isUpdating">修改条件分组</h3>
<form method="post" class="ui form" data-tea-success="success" data-tea-action="$">
<input type="hidden" name="condGroupJSON" :value="JSON.stringify(group)"/>
<table class="ui table definition selectable">
<tr>
<td class="title">子条件列表</td>
<td style="word-break: break-all">
<div v-if="group.conds.length > 0">
<var v-for="(cond, index) in group.conds" style="font-style: normal;display: inline-block; margin-bottom:0.5em">
<span class="ui label small">
<var v-if="cond.type.length == 0 || cond.type == 'params'" style="font-style: normal">{{cond.param}} <var>{{cond.operator}}</var></var>
<var v-if="cond.type.length > 0 && cond.type != 'params'" style="font-style: normal">{{typeName(cond)}}: </var>
{{cond.value}}
<sup v-if="cond.isCaseInsensitive" title="不区分大小写"><i class="icon info small"></i></sup> &nbsp;
<a href="" title="修改" @click.prevent="updateCond(index, cond)"><i class="icon pencil small"></i></a> <a href="" title="删除" @click.prevent="removeCond(index)"><i class="icon remove"></i></a> </span>
<var v-if="index < group.conds.length - 1"> {{group.connector}} &nbsp;</var>
</var>
<div class="ui divider"></div>
</div>
<button class="ui button tiny" type="button" @click.prevent="addCond()">+</button>
</td>
</tr>
<tr>
<td>子条件之间关系</td>
<td>
<select class="ui dropdown auto-width" v-model="group.connector">
<option value="and"></option>
<option value="or"></option>
</select>
<p class="comment" v-if="group.connector == 'and'">必须满足所有条件才能成立。</p>
<p class="comment" v-if="group.connector == 'or'">只要满足其中一个条件即可成立。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>补充说明</td>
<td>
<textarea rows="3" v-model="group.description" maxlength="100"></textarea>
</td>
</tr>
<tr>
<td>启用当前分组</td>
<td>
<div class="ui checkbox">
<input type="checkbox" v-model="group.isOn"/>
<label></label>
</div>
</td>
</tr>
</tbody>
</table>
<submit-btn>确定</submit-btn>
</form>

View File

@@ -0,0 +1,72 @@
Tea.context(function () {
this.success = NotifyPopup
this.group = {
connector: "and", // 默认为and更符合用户的直觉
description: "",
isReverse: false,
conds: [],
isOn: true
}
this.isUpdating = false
// 是否在修改
this.$delay(function () {
if (window.parent.UPDATING_COND_GROUP != null) {
this.group = window.parent.UPDATING_COND_GROUP
this.isUpdating = true
} else if (this.group.conds.length == 0) {
// 如果尚未有条件,则自动弹出添加界面
this.addCond()
}
})
// 条件类型名称
this.typeName = function (cond) {
let c = this.components.$find(function (k, v) {
return v.type == cond.type
})
if (c != null) {
return c.name;
}
return cond.param + " " + cond.operator
}
// 添加条件
this.addCond = function () {
window.UPDATING_COND = null
let that = this
teaweb.popup("/servers/server/settings/conds/addCondPopup", {
width: "32em",
height: "22em",
callback: function (resp) {
that.group.conds.push(resp.data.cond)
}
})
}
// 删除条件
this.removeCond = function (condIndex) {
let that = this
teaweb.confirm("确定要删除此条件?", function () {
that.group.conds.$remove(condIndex)
})
}
// 修改条件
this.updateCond = function (condIndex, cond) {
window.UPDATING_COND = cond
let that = this
teaweb.popup("/servers/server/settings/conds/addCondPopup", {
width: "32em",
height: "22em",
callback: function (resp) {
Vue.set(that.group.conds, condIndex, resp.data.cond)
}
})
}
})

View File

@@ -0,0 +1,5 @@
.checkboxes .checkbox {
width: 8em;
margin-bottom: 0.5em;
}
/*# sourceMappingURL=index.css.map */

View File

@@ -0,0 +1 @@
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,WACC;EACC,UAAA;EACA,oBAAA","file":"index.css"}

View File

@@ -0,0 +1,107 @@
{$layout "layout_popup"}
<h3>批量复制配置</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success" data-tea-done="done" data-tea-before="before">
<csrf-token></csrf-token>
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="configCode" :value="configCode"/>
<input type="hidden" name="targetsJSON" :value="JSON.stringify(targets)"/>
<table class="ui table definition selectable">
<tbody>
<tr>
<td class="title">将当前配置复制到</td>
<td>
<select class="ui dropdown auto-width" v-model="targetType" @change="changeTargetType">
<option value="">[选择目标]</option>
<option v-for="option in targetOptions" :value="option.code">{{option.name}}</option>
</select>
<p class="comment" v-if="configCode == 'waf'">目前只能同步WAF设置的启用和停用不能复制规则、IP名单等。</p>
</td>
</tr>
</tbody>
<!-- WAF特殊选项 -->
<tbody v-show="configCode == 'waf'">
<tr>
<td>同时拷贝国家/地区/省份封禁</td>
<td>
<checkbox name="wafCopyRegions"></checkbox>
<p class="comment">WAF设置专有选项。</p>
</td>
</tr>
</tbody>
<!-- 选择分组 -->
<tbody v-show="targetType == 'group'">
<tr v-show="userId > 0">
<td>当前用户下分组</td>
<td>
<div class="checkboxes">
<checkbox v-for="userGroup in userGroups" :v-value="userGroup.id" @input="changeGroupId(userGroup.id)">{{userGroup.name}}</checkbox>
</div>
</td>
</tr>
<tr>
<td>管理员创建的分组</td>
<td>
<div class="checkboxes">
<checkbox v-for="adminGroup in adminGroups" :v-value="adminGroup.id" @input="changeGroupId(adminGroup.id)">{{adminGroup.name}}</checkbox>
</div>
</td>
</tr>
</tbody>
<!-- 选择用户 -->
<tbody v-show="targetType == 'user'">
<tr>
<td>选择用户</td>
<td>
<user-selector @change="changeUser"></user-selector>
</td>
</tr>
</tbody>
<!-- 选择集群 -->
<tbody v-show="targetType == 'cluster'">
<tr>
<td>选择集群</td>
<td>
<node-cluster-combo-box @change="changeCluster"></node-cluster-combo-box>
</td>
</tr>
</tbody>
<!-- 选择网站 -->
<tbody v-show="targetType == 'server'">
<tr>
<td>当前用户下网站</td>
<td></td>
</tr>
<tr>
<td>管理员创建的网站</td>
<td></td>
</tr>
</tbody>
<!-- 网站数量 -->
<tbody>
<tr>
<td>目标网站数量</td>
<td>
<span v-if="countServers < 0">-</span>
<span v-else><strong>{{countServers}}个</strong></span>
</td>
</tr>
<tr v-if="countServers > 0">
<td>提醒</td>
<td><span class="red">此操作无法撤销,请谨慎确认!</span></td>
</tr>
</tbody>
</table>
<submit-btn v-show="!isRequesting">确认复制</submit-btn>
<button class="ui button disabled" type="button" v-show="isRequesting">正在复制...</button>
</form>

View File

@@ -0,0 +1,71 @@
Tea.context(function () {
this.targetType = ""
this.countServers = -1
this.targets = []
this.reloadCountServers = function () {
this.countServers = -1
this.$post(".countServers")
.params({
"targets": this.targets
})
.success(function (resp) {
this.countServers = resp.data.countServers
})
}
this.changeTargetType = function () {
this.targets = []
this.countServers = -1
if (this.targetType.match(/^(user|cluster):/)) {
this.targets = [this.targetType]
this.reloadCountServers()
}
}
this.changeUser = function (userId) {
if (typeof userId == "number" && userId > 0) {
this.targets = ["user:" + userId]
this.reloadCountServers()
} else {
this.targets = []
this.countServers = -1
}
}
this.changeCluster = function (clusterId) {
if (typeof clusterId == "number" && clusterId > 0) {
this.targets = ["cluster:" + clusterId]
this.reloadCountServers()
} else {
this.targets = []
this.countServers = -1
}
}
let groupIds = []
this.changeGroupId = function (groupId) {
if (groupIds.$contains(groupId)) {
groupIds.$removeValue(groupId)
} else {
groupIds.push(groupId)
}
if (groupIds.length > 0) {
this.targets = ["groups:" + groupIds.join(",")]
this.reloadCountServers()
} else {
this.targets = []
this.countServers = -1
}
}
this.isRequesting = false
this.before = function () {
this.isRequesting = true
}
this.done = function () {
this.isRequesting = false
}
})

View File

@@ -0,0 +1,6 @@
.checkboxes {
.checkbox {
width: 8em;
margin-bottom: 0.5em;
}
}

View File

@@ -0,0 +1,28 @@
{$layout}
{$template "../settings_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="serverId" :value="serverId"/>
<table class="ui table selectable definition">
<tr>
<td class="title">当前网站CNAME</td>
<td>
<span id="cname-text">{{dnsName}}.<span v-if="dnsDomain.length > 0">{{dnsDomain}}</span><span v-else>根域名</span></span> &nbsp; <copy-to-clipboard :v-target="'cname-text'"></copy-to-clipboard> &nbsp;<a href="" @click.prevent="regenerateCNAME()" style="font-size: 0.8em">[重新生成]</a> &nbsp; <a href="" @click.prevent="updateCNAME()" style="font-size: 0.8em">[手动修改]</a>
<p class="comment">你需要为你的每个<a :href="'/servers/server/settings/serverNames?serverId=' + serverId">网站域名</a>设置一个CNAME解析值为上面内容。</p>
<p v-if="dnsDomain.length == 0"><span class="red">你尚未为当前网站所在集群指定CNAME根域名可以在 <a :href="'/clusters/cluster/settings/dns?clusterId=' + server.clusterId" target="_blank">[这里]</a> 修改。</span></p>
</td>
</tr>
<tr>
<td>支持任意域名CNAME</td>
<td>
<checkbox name="supportCNAME" v-model="supportCNAME"></checkbox>
<p class="comment">选中后表示允许任意域名使用此服务的CNAME直接访问此服务。需要节点服务器可以正确解析DNS记录。在严格匹配域名时才会生效。此选项可能导致安全问题请谨慎开启。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,22 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.regenerateCNAME = function () {
let serverId = this.serverId
teaweb.confirm("确定要重新生成此服务的CNAME吗", function () {
this.$post(".regenerateCNAME")
.params({
serverId: serverId
})
.refresh()
})
}
this.updateCNAME = function () {
teaweb.popup("/servers/server/settings/dns/updateCNAMEPopup?serverId=" + this.serverId, {
callback: function () {
teaweb.successRefresh("保存成功")
}
})
}
})

View File

@@ -0,0 +1,20 @@
{$layout "layout_popup"}
<h3>修改服务CNAME</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="serverId" :value="serverId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">CNAME *</td>
<td>
<input type="text" name="dnsName" maxlength="30" v-model="dnsName"/>
<p class="comment">英文字母、数字的组合最长30个字符。</p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,78 @@
{$layout "layout_popup"}
<h3>添加Fastcgi服务</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="paramsJSON" :value="JSON.stringify(params)"/>
<table class="ui table definition selectable">
<tr>
<td class="title">Fastcgi地址</td>
<td>
<input type="text" name="address" placeholder="比如 127.0.0.1:9000" maxlength="100" style="width:14em" ref="focus"/>
</td>
</tr>
<tr>
<td>自定义参数集</td>
<td>
<div v-for="(param, index) in params">
<div class="ui field" style="margin:0" v-if="param.nameZh.length > 0"><label>{{param.nameZh}}</label></div>
<div class="ui fields inline" >
<div class="ui field">
<input type="text" name="paramNames" placeholder="参数名" v-model="param.name" style="width:12em" />
</div>
<div class="ui field">
<input type="text" name="paramValues" placeholder="参数值" v-model="param.value" style="width:16em"/>
</div>
<div class="ui field" style="padding:0">
<a href="" title="删除" @click.prevent="removeParam(index)"><i class="ui icon remove"></i> </a>
</div>
</div>
</div>
<p class="comment" v-if="params.length > 0">可以在参数值中使用一些变量<a href="https://goedge.cn/docs/Server/Variables.md" target="_blank">点这里查看</a></p>
<button class="ui button tiny" type="button" @click.prevent="addParam()">+</button>
</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 right labeled input" style="width:7em">
<input type="number" name="readTimeout" maxlength="10" placeholder="比如 30"/>
<span class="ui basic label"></span>
</div>
</td>
</tr>
<tr>
<td>连接池尺寸</td>
<td>
<input type="text" name="poolSize" value="0" maxlength="4" style="width:7em"/>
<p class="comment">0表示不限制通常可以设置为CPU数量的两倍</p>
</td>
</tr>
<tr>
<td>PATH_INFO匹配</td>
<td>
<input type="text" name="pathInfoPattern" maxlength="100"/>
<p class="comment">匹配PATH_INFO的正则表达式用括号表示匹配的内容如果只有一个匹配括号表示第一个括号为${fastcgi.pathInfo}值;如果有两个或两个以上的匹配括号,则第一个表示匹配的是${fastcgi.filename},第二个匹配的是${fastcgi.pathInfo},比如(\w+\.php)(.+)$。</p>
</td>
</tr>
<tr>
<td>启用当前Fastcgi</td>
<td>
<checkbox name="isOn" value="1" checked="checked"></checkbox>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,29 @@
Tea.context(function () {
this.params = [
{
"name": "DOCUMENT_ROOT",
"value": "",
"nameZh": "文档目录"
},
{
"name": "SCRIPT_FILENAME",
"value": "",
"nameZh": "脚本文件"
}
]
this.addParam = function () {
this.params.push({
"name": "",
"value": "",
"nameZh": ""
})
this.$delay(function () {
this.$find("form input[name='paramNames']").last().focus()
})
}
this.removeParam = function (index) {
this.params.$remove(index)
}
})

View File

@@ -0,0 +1,11 @@
{$layout}
{$template "../settings_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="webId" :value="webId"/>
<http-fastcgi-box :v-fastcgi-ref="fastcgiRef" :v-fastcgi-configs="fastcgiConfigs"></http-fastcgi-box>
<submit-btn></submit-btn>
</form>
</div>

View File

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

View File

@@ -0,0 +1,79 @@
{$layout "layout_popup"}
<h3>修改Fastcgi服务</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="fastcgiId" :value="fastcgi.id"/>
<input type="hidden" name="paramsJSON" :value="JSON.stringify(params)"/>
<table class="ui table definition selectable">
<tr>
<td class="title">Fastcgi地址</td>
<td>
<input type="text" name="address" placeholder="比如 127.0.0.1:9000" maxlength="100" style="width:14em" ref="focus" v-model="fastcgi.address"/>
</td>
</tr>
<tr>
<td>自定义参数集</td>
<td>
<div v-for="(param, index) in params">
<div class="ui field" style="margin:0" v-if="param.nameZh != null && param.nameZh.length > 0"><label>{{param.nameZh}}</label></div>
<div class="ui fields inline" >
<div class="ui field">
<input type="text" name="paramNames" placeholder="参数名" v-model="param.name" style="width:12em" />
</div>
<div class="ui field">
<input type="text" name="paramValues" placeholder="参数值" v-model="param.value" style="width:16em"/>
</div>
<div class="ui field" style="padding:0">
<a href="" title="删除" @click.prevent="removeParam(index)"><i class="ui icon remove"></i> </a>
</div>
</div>
</div>
<p class="comment" v-if="params.length > 0">可以在参数值中使用一些变量<a href="https://goedge.cn/docs/Server/Variables.md" target="_blank">点这里查看</a></p>
<button class="ui button tiny" type="button" @click.prevent="addParam()">+</button>
</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 right labeled input" style="width:7em">
<input type="number" name="readTimeout" maxlength="10" placeholder="比如 30" v-model="fastcgi.readTimeout.count"/>
<span class="ui basic label"></span>
</div>
</td>
</tr>
<tr>
<td>连接池尺寸</td>
<td>
<input type="text" name="poolSize" value="0" maxlength="4" style="width:7em" v-model="fastcgi.poolSize"/>
<p class="comment">0表示不限制通常可以设置为CPU数量的两倍</p>
</td>
</tr>
<tr>
<td>PATH_INFO匹配</td>
<td>
<input type="text" name="pathInfoPattern" maxlength="100" v-model="fastcgi.pathInfoPattern"/>
<p class="comment">匹配PATH_INFO的正则表达式用括号表示匹配的内容如果只有一个匹配括号表示第一个括号为${fastcgi.pathInfo}值;如果有两个或两个以上的匹配括号,则第一个表示匹配的是${fastcgi.filename},第二个匹配的是${fastcgi.pathInfo},比如(\w+\.php)(.+)$。</p>
</td>
</tr>
<tr>
<td>启用当前Fastcgi</td>
<td>
<checkbox name="isOn" value="1" v-model="fastcgi.isOn"></checkbox>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,33 @@
Tea.context(function () {
this.params = this.fastcgi.params
if (this.params == null) {
this.params = []
} else {
this.params.forEach(function (v) {
switch (v.name) {
case "DOCUMENT_ROOT":
v.nameZh = "文档目录"
break;
case "SCRIPT_FILENAME":
v.nameZh = "脚本文件"
break
}
})
}
this.addParam = function () {
this.params.push({
"name": "",
"value": "",
"nameZh": ""
})
this.$delay(function () {
this.$find("form input[name='paramNames']").last().focus()
})
}
this.removeParam = function (index) {
this.params.$remove(index)
}
})

View File

@@ -0,0 +1,16 @@
{$layout "layout_popup"}
<h3>添加需要删除的报头</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="headerPolicyId" :value="headerPolicyId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">报头名称<em>Name</em></td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" v-model="headerName"/>
<p class="comment"><http-header-assistant :v-type="type" :v-value="headerName" @select="selectHeader"></http-header-assistant>请注意报头名称的大小写,如无特殊需求,报头名称的格式通常为<code-label>Xxx</code-label>或者<code-label>Xxx-Yyy</code-label></p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,7 @@
Tea.context(function () {
this.headerName = ""
this.selectHeader = function (headerName) {
this.headerName = headerName
}
})

View File

@@ -0,0 +1,16 @@
{$layout "layout_popup"}
<h3>添加非标报头</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="headerPolicyId" :value="headerPolicyId"/>
<table class="ui table definition selectable">
<tr>
<td class="title">报头名称<em>Name</em></td>
<td>
<input type="text" name="name" maxlength="100" ref="focus" v-model="headerName"/>
<p class="comment">比如<code-label>hello_world</code-label></p>
</td>
</tr>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,7 @@
Tea.context(function () {
this.headerName = ""
this.selectHeader = function (headerName) {
this.headerName = headerName
}
})

View File

@@ -0,0 +1,72 @@
{$layout "layout_popup"}
<h3 v-if="type == 'request'">添加自定义请求报头</h3>
<h3 v-if="type == 'response'">添加自定义响应报头</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="headerPolicyId" :value="headerPolicyId"/>
<input type="hidden" name="type" :value="type"/>
<table class="ui table definition selectable">
<tr>
<td class="title">报头名称<em>Name</em> *</td>
<td>
<input type="text" name="name" value="" maxlength="200" placeholder="类似于Server、Content-Type之类" ref="focus" v-model="headerName" autocomplete="off"/>
<p class="comment"><http-header-assistant :v-type="type" :v-value="headerName" @select="selectHeader"></http-header-assistant>请注意报头名称的大小写,如无特殊需求,报头名称的格式通常为<code-label>Xxx</code-label>或者<code-label>Xxx-Yyy</code-label></p>
</td>
</tr>
<tr>
<td><em>Value</em></td>
<td>
<input type="text" name="value" maxlength="500"/>
<p class="comment">可以包含请求变量。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator>更多选项</more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr v-show="type == 'response'">
<td>支持的状态码</td>
<td>
<http-status-box></http-status-box>
<p class="comment">不填表示支持所有的状态码。</p>
</td>
</tr>
<tr>
<td>支持的请求方法</td>
<td><http-methods-box></http-methods-box></td>
</tr>
<tr>
<td>支持的域名</td>
<td><domains-box></domains-box></td>
</tr>
<tr>
<td>只附加不替换</td>
<td>
<checkbox name="shouldAppend"></checkbox>
<p class="comment">选中后表示如果已经存在同名的报头,则只会附加在原有的报头之后,不会覆盖。</p>
</td>
</tr>
<tr v-show="type == 'response'">
<td>不在跳转时启用</td>
<td>
<checkbox name="disableRedirect"></checkbox>
<p class="comment">选中后表示在30X跳转时不启用当前报头。</p>
</td>
</tr>
<tr>
<td class="color-border">启用内容替换</td>
<td>
<checkbox name="shouldReplace" v-model="shouldReplace"></checkbox>
<p class="comment">可以替换原有报头中的内容。</p>
</td>
</tr>
<tr v-show="shouldReplace">
<td class="color-border">替换内容</td>
<td>
<http-header-replace-values></http-header-replace-values>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,8 @@
Tea.context(function () {
this.shouldReplace = false
this.headerName = ""
this.selectHeader = function (headerName) {
this.headerName = headerName
}
})

View File

@@ -0,0 +1,14 @@
{$layout}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div class="margin"></div>
<http-header-policy-box
:v-request-header-policy="requestHeaderPolicy"
:v-response-header-policy="responseHeaderPolicy"
:v-params="'serverId=' + serverId"
:v-has-group-request-config="hasGroupRequestConfig"
:v-has-group-response-config="hasGroupResponseConfig"
:v-group-setting-url="groupSettingURL"></http-header-policy-box>
</div>

View File

@@ -0,0 +1,9 @@
{$layout "layout_popup"}
<h3>CORS跨域设置</h3>
<form class="ui form" data-tea-action="$" data-tea-success="success">
<csrf-token></csrf-token>
<input type="hidden" name="headerPolicyId" :value="headerPolicyId"/>
<http-cors-header-config-box v-model="cors"></http-cors-header-config-box>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,73 @@
{$layout "layout_popup"}
<h3 v-if="type == 'request'">修改自定义请求报头</h3>
<h3 v-if="type == 'response'">修改自定义响应报头</h3>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="headerPolicyId" :value="headerPolicyId"/>
<input type="hidden" name="headerId" :value="headerId"/>
<input type="hidden" name="type" :value="type"/>
<table class="ui table definition selectable">
<tr>
<td class="title">报头名称<em>Name</em> *</td>
<td>
<input type="text" name="name" value="" v-model="headerConfig.name" maxlength="200" placeholder="类似于Server、Content-Type之类" ref="focus"/>
<p class="comment"><http-header-assistant :v-type="type" :v-value="headerConfig.name" @select="selectHeader"></http-header-assistant>请注意报头名称的大小写,如无特殊需求,报头名称的格式通常为<code-label>Xxx</code-label>或者<code-label>Xxx-Yyy</code-label></p>
</td>
</tr>
<tr>
<td><em>Value</em></td>
<td>
<input type="text" name="value" v-model="headerConfig.value" maxlength="500"/>
<p class="comment">可以包含请求变量。</p>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator>更多选项</more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr v-show="type == 'response'">
<td>支持的状态码</td>
<td>
<http-status-box :v-status-list="statusList"></http-status-box>
<p class="comment">不填表示支持所有的状态码。</p>
</td>
</tr>
<tr>
<td>支持的请求方法</td>
<td><http-methods-box :v-methods="headerConfig.methods"></http-methods-box></td>
</tr>
<tr>
<td>支持的域名</td>
<td><domains-box :v-domains="headerConfig.domains"></domains-box></td>
</tr>
<tr>
<td>只附加不替换</td>
<td>
<checkbox name="shouldAppend" v-model="headerConfig.shouldAppend"></checkbox>
<p class="comment">选中后表示如果已经存在同名的报头,则只会附加在原有的报头之后,不会覆盖。</p>
</td>
</tr>
<tr v-show="type == 'response'">
<td>不在跳转时启用</td>
<td>
<checkbox name="disableRedirect" v-model="headerConfig.disableRedirect"></checkbox>
<p class="comment">选中后表示在30X跳转时不启用当前报头。</p>
</td>
</tr>
<tr>
<td class="color-border">启用内容替换</td>
<td>
<checkbox name="shouldReplace" v-model="shouldReplace"></checkbox>
<p class="comment">可以替换原有报头中的内容。</p>
</td>
</tr>
<tr v-show="shouldReplace">
<td class="color-border">替换内容</td>
<td>
<http-header-replace-values :v-replace-values="headerConfig.replaceValues"></http-header-replace-values>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>

View File

@@ -0,0 +1,11 @@
Tea.context(function () {
this.shouldReplace = this.headerConfig.shouldReplace
this.statusList = []
if (this.headerConfig.status != null && this.headerConfig.status.codes != null) {
this.statusList = this.headerConfig.status.codes
}
this.selectHeader = function (headerName) {
this.headerConfig.name = headerName
}
})

View File

@@ -0,0 +1,39 @@
{$layout}
{$template "../settings_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="serverId" :value="serverId"/>
<input type="hidden" name="webId" :value="webId"/>
<input type="hidden" name="serverType" :value="serverType"/>
<table class="ui table selectable definition">
<tr>
<td class="title">启用HTTP</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" v-model="httpConfig.isOn"/>
<label></label>
</div>
</td>
</tr>
<tbody v-show="httpConfig.isOn">
<tr>
<td class="title">绑定端口 *</td>
<td>
<span class="red" v-if="httpConfig.isOn && (httpConfig.addresses == null || httpConfig.addresses.length == 0)">还没有添加端口绑定会导致HTTP服务无法访问。</span>
<network-addresses-box :v-server-type="serverType" :v-addresses="httpConfig.addresses" :v-protocol="'http'"></network-addresses-box>
<p class="comment"><span v-if="conflictingPorts.length > 0" class="red">配置错误:<span v-for="(port, index) in conflictingPorts">{{port}}<span v-if="index != conflictingPorts.length - 1"></span></span><span v-if="conflictingPorts.length > 1"></span>端口同HTTPS设置的端口冲突请删除HTTP或HTTPS中的相关端口。</span></p>
</td>
</tr>
<tr>
<td>自动跳转到HTTPS</td>
<td>
<http-redirect-to-https-box :v-redirect-to-https-config="redirectToHTTPSConfig"></http-redirect-to-https-box>
</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,48 @@
{$layout}
{$var "header"}
<script src="/servers/certs/datajs" type="text/javascript"></script>
<script src="/js/sortable.min.js" type="text/javascript"></script>
{$end}
{$template "../settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<p class="comment">提醒HTTP/2、证书等信息修改后可能需要清空浏览器缓存后才能浏览效果。</p>
<div v-if="httpsConfig.isOn && missingCertServerNames != null" class="ui message warning">
警告当前网站绑定的以下域名尚未配置证书将无法通过HTTPS协议访问{{missingCertServerNames.join("、")}} 。
</div>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="serverId" :value="serverId"/>
<input type="hidden" name="serverType" :value="serverType"/>
<table class="ui table selectable definition">
<tr>
<td class="title">启用HTTPS</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" v-model="httpsConfig.isOn"/>
<label></label>
</div>
</td>
</tr>
<tbody v-show="httpsConfig.isOn">
<tr>
<td class="title">绑定端口 *</td>
<td>
<span class="red" v-if="httpsConfig.isOn && (httpsConfig.addresses == null || httpsConfig.addresses.length == 0)">还没有添加端口绑定会导致HTTPS服务无法访问。</span>
<network-addresses-box :v-server-type="serverType" :v-addresses="httpsConfig.addresses" :v-protocol="'https'"></network-addresses-box>
<p class="comment"><span v-if="conflictingPorts.length > 0" class="red">配置错误:<span v-for="(port, index) in conflictingPorts">{{port}}<span v-if="index != conflictingPorts.length - 1"></span></span><span v-if="conflictingPorts.length > 1"></span>端口同HTTP设置的端口冲突请删除HTTP或HTTPS中的相关端口。</span></p>
</td>
</tr>
</tbody>
</table>
<!-- SSL配置 -->
<ssl-config-box :v-ssl-policy="httpsConfig.sslPolicy" :v-protocol="'https'" v-show="httpsConfig.isOn" :v-server-id="serverId" :v-support-http3="httpsConfig.supportsHTTP3"></ssl-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,43 @@
{$layout "layout_popup"}
<h3>申请免费证书</h3>
<form method="post" class="ui form" data-tea-success="success" data-tea-action="$" data-tea-timeout="300" data-tea-before="beforeSubmit" data-tea-fail="fail">
<csrf-token></csrf-token>
<table class="ui table definition selectable">
<tr>
<td class="title">证书包含的域名 *</td>
<td>
<span v-if="serverNames.length == 0" class="disabled">还没有需要申请证书的域名,暂时不能申请。</span>
<div v-if="serverNames.length > 0">
<div v-for="(serverName, index) in serverNames" class="ui tiny basic label">
<input type="hidden" name="serverNames" :value="serverName"/>
{{serverName}}
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon remove"></i></a>
</div>
</div>
</td>
</tr>
<tr>
<td>证书用户 *</td>
<td>
<div v-if="users.length > 0">
<select class="ui dropdown auto-width" name="userId" v-model="userId">
<option value="0">[请选择]</option>
<option v-for="user in users" :value="user.id">{{user.email}}{{user.description}}</option>>
</select>
<p class="comment">用来申请证书的用户。</p>
</div>
<div v-if="users.length == 0">
<input type="text" name="userEmail" maxlength="100" placeholder="用户E-mail" ref="focus"/>
<p class="comment">用来申请证书的用户邮箱,可以任意填写,只要格式正确即可。</p>
</div>
</td>
</tr>
</table>
<submit-btn v-if="!isRequesting">提交</submit-btn>
<button class="ui button" type="button" v-if="isRequesting">处理中...</button>
</form>

View File

@@ -0,0 +1,25 @@
Tea.context(function () {
this.isRequesting = false
this.userId = 0
this.remove = function (index) {
this.serverNames.$remove(index)
}
this.beforeSubmit = function () {
this.isRequesting = true
}
this.fail = function (resp) {
this.isRequesting = false
teaweb.warn(resp.message)
if (resp.data.acmeUser != null) {
this.users.push({
id: resp.data.acmeUser.id,
email: resp.data.acmeUser.email,
description: ""
})
this.userId = resp.data.acmeUser.id
}
}
})

View File

@@ -0,0 +1,101 @@
{$layout}
{$template "settings_menu"}
{$template "/left_menu_with_menu"}
<div class="right-box with-menu">
<div v-if="server.trafficLimitStatus != null">
<div class="ui margin"></div>
<warning-message>
<server-traffic-limit-status-viewer v-model="server.trafficLimitStatus"></server-traffic-limit-status-viewer>
</warning-message>
</div>
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="serverId" :value="server.id"/>
<table class="ui table selectable definition">
<tr>
<td>所属用户</td>
<td>
<span v-if="user != null">{{user.fullname}} <span class="small">{{user.username}}</span><link-icon :href="'/users/user?userId=' + user.id"></link-icon></span>
<div v-if="user == null">
<div v-show="!userSelectorVisible">
<span class="disabled">没有指定用户</span> &nbsp; <a href="" @click.prevent="showUserSelector">[指定用户]</a>
</div>
<div v-show="userSelectorVisible">
<user-selector style="display:inline-block"></user-selector>
<p class="comment"><span class="red">此操作同时会将与当前网站相关联的证书等数据自动修改为此用户所属。</span></p>
</div>
</div>
</td>
</tr>
<tr>
<td class="title">网站名称 *</td>
<td>
<input type="text" name="name" maxlength="60" ref="focus" v-model="server.name"/>
</td>
</tr>
<tr v-if="plans.length > 0">
<td>选择套餐</td>
<td>
<select class="ui dropdown auto-width" name="userPlanId" v-model="userPlanId">
<option value="0">[选择套餐]</option>
<option v-for="plan in plans" :value="plan.id">{{plan.name}}{{plan.dayTo}}</option>
</select>
</td>
</tr>
<tr v-if="plans.length == 0 && teaIsPlus">
<td>选择套餐</td>
<td>当前网站所属用户没有可用套餐。</td>
</tr>
<tr>
<td :class="{'color-border':server.clusterId != oldClusterId}">部署的集群 *</td>
<td>
<select class="ui dropdown auto-width" name="clusterId" v-model="server.clusterId" >
<option v-for="cluster in clusters" :value="cluster.id">{{cluster.name}}</option>
</select>
</td>
</tr>
<tr v-show="server.clusterId != oldClusterId">
<td class="color-border">是否保留原集群配置</td>
<td>
<checkbox name="keepOldConfigs" checked="checked"></checkbox>
<p class="comment">选中表示在先前的集群节点上仍然保留当前网站的配置,直至节点配置全部刷新时才会删除;不选中,则表示立即删除原集群上关于当前网站的配置。</p>
</td>
</tr>
<tr>
<td>网站类型 *</td>
<td>
{{typeName}}
</td>
</tr>
<tr>
<td>选择分组</td>
<td>
<server-group-selector :v-groups="server.groups"></server-group-selector>
</td>
</tr>
<tr>
<td colspan="2"><more-options-indicator></more-options-indicator></td>
</tr>
<tbody v-show="moreOptionsVisible">
<tr>
<td>描述</td>
<td>
<textarea name="description" rows="3" v-model="server.description"></textarea>
</td>
</tr>
<tr>
<td>启用当前网站</td>
<td>
<div class="ui checkbox">
<input type="checkbox" name="isOn" value="1" v-model="server.isOn"/>
<label></label>
</div>
<p class="comment">可以使用此选项整体关闭当前网站。</p>
</td>
</tr>
</tbody>
</table>
<submit-btn></submit-btn>
</form>
</div>

View File

@@ -0,0 +1,43 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
/**
* 用户相关
*/
this.userSelectorVisible = false
this.showUserSelector = function () {
this.userSelectorVisible = !this.userSelectorVisible
}
this.userId = 0
this.plans = []
this.userPlanId = 0
this.oldClusterId = this.server.clusterId
if (this.userPlan != null) {
this.userPlanId = this.userPlan.id
}
this.changeUserId = function (v) {
this.userId = v
if (this.userId == 0) {
this.plans = []
return
}
this.$post("/servers/users/plans")
.params({
userId: this.userId,
serverId: this.serverId
})
.success(function (resp) {
this.plans = resp.data.plans
})
}
if (this.user != null) {
this.changeUserId(this.user.id)
}
})

View File

@@ -0,0 +1,9 @@
<div class="margin"></div>
<div class="left-box tiny">
<div class="ui menu text blue vertical tiny">
<a class="item" v-for="item in tinyLeftMenuItems" :href="item.url" :class="{active:item.isActive, separator:item.name == '-', on:item.isOn, off:item.isOff||item.isImportant}">
<i class="icon play tiny" :style="{'visibility':item.isActive ? 'visible' : 'hidden'}"></i><span v-if="item.name != '-'">{{item.name}}<var v-if="item.isOff"></var><var v-if="item.isImportant"></var></span>
</a>
</div>
</div>

View File

@@ -0,0 +1,7 @@
<first-menu>
<menu-item :href="'/servers/server/settings/locations?serverId=' + serverId">&laquo; 返回网站设置</menu-item>
<span class="item disabled" style="padding-left:0;padding-right:0">|</span>
<menu-item :href="'/servers/server/settings/locations?serverId=' + serverId">路由规则</menu-item>
<raquo-item></raquo-item>
<menu-item :href="'/servers/server/settings/locations/location?serverId=' + serverId + '&locationId=' + locationId" :active="true">{{locationConfig.pattern}}</menu-item>
</first-menu>

View File

@@ -0,0 +1,4 @@
<first-menu>
<menu-item :href="'/servers/server/settings/locations?serverId=' + serverId" code="index">列表</menu-item>
<menu-item :href="'/servers/server/settings/locations/create?serverId=' + serverId" code="create">创建</menu-item>
</first-menu>

View File

@@ -0,0 +1,16 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$template "../location_menu"}
{$template "../left_menu"}
<div class="right-box tiny">
<form class="ui form" data-tea-action="$" data-tea-success="success" ref="authForm">
<input type="hidden" name="webId" :value="webId">
<http-auth-config-box :v-auth-config="authConfig" :v-is-location="true"
@change="changeMethods"></http-auth-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

@@ -0,0 +1,10 @@
Tea.context(function () {
this.success = NotifyReloadSuccess("保存成功")
this.changeMethods = function (config) {
Tea.action("$")
.form(this.$refs.authForm)
.post()
teaweb.successRefresh("保存成功")
}
})

View File

@@ -0,0 +1,20 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$template "../location_menu"}
{$template "../left_menu"}
<div class="right-box tiny">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<http-access-log-config-box
:v-access-log-config="accessLogConfig"
:v-fields="fields"
:v-default-field-codes="defaultFieldCodes"
:v-is-location="true"></http-access-log-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,17 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$template "../location_menu"}
{$template "../left_menu"}
<div class="right-box tiny">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<http-cache-config-box :v-cache-config="cacheConfig" :v-cache-policy="cachePolicy" :v-is-location="true"></http-cache-config-box>
<submit-btn></submit-btn>
<p class="comment">修改条件设置后请记得保存。</p>
</form>
</div>
</div>

View File

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

View File

@@ -0,0 +1,18 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$template "../location_menu"}
{$template "../left_menu"}
<div class="right-box tiny">
<form method="post" class="ui form" data-tea-action="$" data-tea-success="success">
<input type="hidden" name="webId" :value="webId"/>
<csrf-token></csrf-token>
<http-cc-config-box :v-cc-config="ccConfig" :v-is-location="true"></http-cc-config-box>
<submit-btn></submit-btn>
</form>
</div>
</div>

View File

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

Some files were not shown because too many files have changed in this diff Show More