Initial commit (code only without large binaries)

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

View File

@@ -0,0 +1,56 @@
{$layout}
{$var "header"}
<!-- datepicker -->
<script type="text/javascript" src="/js/moment.min.js"></script>
<script type="text/javascript" src="/js/pikaday.js"></script>
<link rel="stylesheet" href="/js/pikaday.css"/>
<link rel="stylesheet" href="/js/pikaday.theme.css"/>
<link rel="stylesheet" href="/js/pikaday.triangle.css"/>
{$end}
{$template "/left_menu"}
<div class="right-box">
{$ if eq .featureIsOn false }
<server-feature-required></server-feature-required>
{$ else }
<first-menu>
<menu-item :href="path + '?serverId=' + serverId + '&day=' + day" :active="hasError == 0">所有日志</menu-item>
<menu-item :href="path + '?serverId=' + serverId + '&day=' + day + '&hasError=1'" :active="hasError > 0">错误日志</menu-item>
<div class="item right">
<form method="get" class="ui form small" :action="path" autocomplete="off">
<input type="hidden" name="serverId" :value="serverId"/>
<div class="ui fields inline">
<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">
<button class="ui button small" type="submit">查找</button>
</div>
</div>
</form>
</div>
</first-menu>
<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="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"></http-access-log-box></td>
</tr>
</table>
<div v-if="accessLogs.length > 0">
<a :href="path + '?serverId=' + serverId + '&requestId=' + lastRequestId + '&day=' + day + '&hasError=' + hasError+'&ip='+ip+'&domain='+domain+'&keyword='+keyword+'&clusterId='+clusterId+'&nodeId='+nodeId" v-if="hasPrev">上一页</a>
<span v-else class="disabled">上一页</span>
<span class="disabled">&nbsp; | &nbsp;</span>
<a :href="path + '?serverId=' + serverId + '&requestId=' + nextRequestId + '&day=' + day + '&hasError=' + hasError+'&ip='+ip+'&domain='+domain+'&keyword='+keyword+'&clusterId='+clusterId+'&nodeId='+nodeId" v-if="hasMore">下一页</a>
<span v-else class="disabled">下一页</span>
</div>
{$end}
</div>

View File

@@ -0,0 +1,21 @@
Tea.context(function () {
this.$delay(function () {
let that = this
if (this.featureIsOn) {
teaweb.datepicker("day-input", function (day) {
that.day = day
})
}
})
let that = this
if (this.featureIsOn) {
this.accessLogs.forEach(function (accessLog) {
if (typeof (that.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = that.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
})
}
})

View File

@@ -0,0 +1,20 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$ if eq .featureIsOn false }
<server-feature-required></server-feature-required>
{$ else }
<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"></http-access-log-box></td>
</tr>
</table>
{$end}
</div>

View File

@@ -0,0 +1,61 @@
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)
// 添加区域信息
this.accessLogs.forEach(function (accessLog) {
if (typeof (resp.data.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = resp.data.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
})
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)
})
}
})

View File

@@ -0,0 +1,33 @@
{$layout}
{$template "/left_menu"}
<div class="right-box">
{$ if eq .featureIsOn false }
<server-feature-required></server-feature-required>
{$ else }
<first-menu>
<menu-item :href="path + '?serverId=' + serverId" :active="hasError == 0">所有日志</menu-item>
<menu-item :href="path + '?serverId=' + serverId + '&hasError=1'" :active="hasError > 0">错误日志</menu-item>
</first-menu>
<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="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"></http-access-log-box></td>
</tr>
</table>
<div v-if="accessLogs.length > 0">
<a :href="path + '?serverId=' + serverId + '&requestId=' + lastRequestId + '&hasError=' + hasError+'&ip='+ip+'&domain='+domain+'&keyword='+keyword+'&clusterId='+clusterId+'&nodeId='+nodeId" v-if="hasPrev">上一页</a>
<span v-else class="disabled">上一页</span>
<span class="disabled">&nbsp; | &nbsp;</span>
<a :href="path + '?serverId=' + serverId + '&requestId=' + nextRequestId + '&hasError=' + hasError+'&ip='+ip+'&domain='+domain+'&keyword='+keyword+'&clusterId='+clusterId+'&nodeId='+nodeId" v-if="hasMore">下一页</a>
<span v-else class="disabled">下一页</span>
</div>
{$end}
</div>

View File

@@ -0,0 +1,12 @@
Tea.context(function () {
let that = this
if (this.featureIsOn) {
this.accessLogs.forEach(function (accessLog) {
if (typeof (that.regions[accessLog.remoteAddr]) == "string") {
accessLog.region = that.regions[accessLog.remoteAddr]
} else {
accessLog.region = ""
}
})
}
})

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,150 @@
{$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>请求URI<em>(RequestURI)</em>{{accessLog.requestURI}}</td>
</tr>
<tr>
<td>请求方法<em>(RequestMethod)</em>{{accessLog.requestMethod}}</td>
<td>主机地址<em>(Host)</em>{{accessLog.host}}</td>
</tr>
<tr>
<td>请求来源<em>(Referer)</em>
<span v-if="accessLog.referer != null && accessLog.referer.length > 0">{{accessLog.referer}}</span>
<span v-else>-</span>
</td>
<td>终端地址<em>(RemoteAddr:RemotePort)</em>{{accessLog.remoteAddr}}:{{accessLog.remotePort}}</td>
</tr>
<tr>
<td>终端信息<em>(UserAgent)</em>
<span v-if="accessLog.userAgent != null && accessLog.userAgent.length > 0">{{accessLog.userAgent}}</span>
<span v-else>-</span>
</td>
<td>协议<em>(Proto)</em>{{accessLog.proto}}</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></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>{{accessLog.bytesSent}}</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规则集{{wafInfo.set.name}}</td>
<td></td>
</tr>
<tr v-if="accessLog.errors != null && accessLog.errors.length > 0">
<td colspan="2">
<div v-for="error in accessLog.errors">
<span class="red">{{error}}</span>
</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>{{accessLog.userAgent}}</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,73 @@
Tea.context(function () {
this.tab = "summary"
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;
}