Skip to content

Commit fc65ca7

Browse files
soso
authored andcommitted
Release v0.1.3
1 parent 76e7ee5 commit fc65ca7

11 files changed

Lines changed: 800 additions & 100 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "emby302gateway-rs"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
edition = "2024"
55
license = "MIT"
66

docs/changelog.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
# 版本更新日志
22

3+
## v0.1.3
4+
5+
发布时间:2026-05-27
6+
7+
### 新增
8+
9+
- 日志页新增“拦截日志明细”视图,放在播放日志之后,用于查看历史被拦截请求。
10+
- 拦截日志改为独立文件持久化到 `data/logs/block.log`,单文件最大 `10MB`,最多保留 `5` 份轮转文件。
11+
- 拦截日志会记录请求拦截、触发封禁、解除封禁三类事件,并保留服务器、用户、IP、请求、封禁方式、到期时间等信息。
12+
- 拦截日志文件输出增加前端同款单行摘要,并在 IP 后附带 IP 归属信息。
13+
14+
### 优化
15+
16+
- 日志页“拦截日志明细”支持按封禁动作筛选,封禁和解除封禁不再显示成普通请求。
17+
- 版本检查成功后改为在左侧版本号旁显示“最新 / 有更新”,不再弹出顶部提示条。
18+
- 更新检查参数兼容 `true/1/yes/on`,避免点击版本号时因 `force` 参数解析失败显示“失败”。
19+
20+
### 验证
21+
22+
- `cargo fmt`
23+
- `cargo check`
24+
- `cd frontend && npx vue-tsc -b --noEmit`
25+
326
## v0.1.2
427

528
发布时间:2026-05-27

frontend/src/App.vue

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ type IpLocation = {
160160
161161
type ProxyRequestDetail = {
162162
id: number
163+
event_type?: 'request' | 'block' | 'unblock'
163164
timestamp_ms: number
164165
server_id: string
165166
server_name: string
@@ -298,7 +299,7 @@ type AuthMode = 'loading' | 'setup' | 'login' | 'app'
298299
type Page = 'home' | 'server' | 'clients' | 'notifications' | 'backup' | 'logs' | 'account'
299300
type ClientStatusFilter = 'all' | 'blocked' | 'allowed'
300301
type LogKindFilter = 'all' | 'playback' | 'general'
301-
type LogViewFilter = 'playback' | 'proxy' | 'general'
302+
type LogViewFilter = 'playback' | 'blocked' | 'proxy' | 'general'
302303
type EncryptionPublicKey =
303304
| { kind: 'webcrypto'; key: CryptoKey }
304305
| { kind: 'forge'; key: forge.pki.rsa.PublicKey }
@@ -461,6 +462,7 @@ const activityLogLoadStep = 80
461462
const requestDetailLoadStep = 100
462463
const logViewLabels: Record<LogViewFilter, string> = {
463464
playback: '播放日志',
465+
blocked: '拦截日志明细',
464466
proxy: '反代请求明细',
465467
general: '信息',
466468
}
@@ -526,12 +528,19 @@ const canLoadMoreGeneralLogs = computed(() =>
526528
&& generalLogLimit.value < activityLogMaxLimit,
527529
)
528530
const canLoadMoreRequestDetails = computed(() =>
529-
selectedLogView.value === 'proxy'
531+
isRequestDetailLogView.value
530532
&& requestDetailRows.value.length >= requestDetailLimit.value
531533
&& requestDetailLimit.value < requestDetailDisplayMax,
532534
)
535+
const isRequestDetailLogView = computed(() => selectedLogView.value === 'proxy' || selectedLogView.value === 'blocked')
533536
const logLevelOptions = computed(() => {
534-
if (selectedLogView.value === 'proxy') {
537+
if (selectedLogView.value === 'blocked') {
538+
return [
539+
{ value: 'all', label: '全部级别' },
540+
{ value: 'blocked', label: 'BLOCKED - 已拦截' },
541+
]
542+
}
543+
if (isRequestDetailLogView.value) {
535544
return [
536545
{ value: 'all', label: '全部级别' },
537546
{ value: 'success', label: 'SUCCESS - 成功' },
@@ -567,11 +576,11 @@ const filteredRequestDetailRows = computed(() =>
567576
}),
568577
)
569578
const visibleLogCount = computed(() => {
570-
if (selectedLogView.value === 'proxy') return filteredRequestDetailRows.value.length
579+
if (isRequestDetailLogView.value) return filteredRequestDetailRows.value.length
571580
return visibleActivityLogRows.value.length
572581
})
573582
const canLoadMoreSelectedLogs = computed(() => {
574-
if (selectedLogView.value === 'proxy') return canLoadMoreRequestDetails.value
583+
if (isRequestDetailLogView.value) return canLoadMoreRequestDetails.value
575584
if (selectedLogView.value === 'general') return canLoadMoreGeneralLogs.value
576585
return canLoadMorePlaybackLogs.value
577586
})
@@ -790,14 +799,7 @@ async function refreshUpdateCheck(force = true) {
790799
updateChecking.value = true
791800
updateCheckError.value = ''
792801
try {
793-
updateCheck.value = await api<UpdateCheck>(`/api/app-info/update-check${force ? '?force=1' : ''}`)
794-
if (force) {
795-
showNotice(
796-
updateCheck.value.has_update
797-
? `发现新版本 ${updateCheck.value.latest_version}`
798-
: `已是最新版本 ${updateCheck.value.current_version}`,
799-
)
800-
}
802+
updateCheck.value = await api<UpdateCheck>(`/api/app-info/update-check${force ? '?force=true' : ''}`)
801803
} catch (err) {
802804
updateCheck.value = null
803805
updateCheckError.value = err instanceof Error ? err.message : String(err)
@@ -1593,7 +1595,10 @@ async function fetchProxyRequestDetails() {
15931595
if (logKeywordFilter.value.trim()) params.set('keyword', logKeywordFilter.value.trim())
15941596
if (logSince.value) params.set('since_ms', String(new Date(logSince.value).getTime()))
15951597
if (logUntil.value) params.set('until_ms', String(new Date(logUntil.value).getTime()))
1596-
return api<ProxyRequestDetail[]>(`/api/monitoring/request-details?${params.toString()}`)
1598+
const endpoint = selectedLogView.value === 'blocked'
1599+
? '/api/monitoring/block-logs'
1600+
: '/api/monitoring/request-details'
1601+
return api<ProxyRequestDetail[]>(`${endpoint}?${params.toString()}`)
15971602
}
15981603
15991604
function loadMorePlaybackLogs() {
@@ -1615,7 +1620,7 @@ function loadMoreRequestDetails() {
16151620
}
16161621
16171622
function loadMoreSelectedLogs() {
1618-
if (selectedLogView.value === 'proxy') return loadMoreRequestDetails()
1623+
if (isRequestDetailLogView.value) return loadMoreRequestDetails()
16191624
if (selectedLogView.value === 'general') return loadMoreGeneralLogs()
16201625
return loadMorePlaybackLogs()
16211626
}
@@ -1628,6 +1633,7 @@ function handleScrollableLogListScroll(event: Event, loadMore: () => void) {
16281633
}
16291634
16301635
function requestSeverity(row: ProxyRequestDetail) {
1636+
if (row.event_type === 'unblock') return 'success'
16311637
if (row.blocked) return 'blocked'
16321638
if (row.cache_hit) return 'cache'
16331639
if (row.status_code >= 500) return 'error'
@@ -1637,6 +1643,8 @@ function requestSeverity(row: ProxyRequestDetail) {
16371643
}
16381644
16391645
function requestSeverityLabel(row: ProxyRequestDetail) {
1646+
if (row.event_type === 'block') return '封禁动作'
1647+
if (row.event_type === 'unblock') return '解除封禁'
16401648
const severity = requestSeverity(row)
16411649
if (severity === 'blocked') return '已拦截'
16421650
if (severity === 'cache') return '缓存'
@@ -1940,14 +1948,22 @@ function logLevelLabel(level: ActivityLogEntry['level']) {
19401948
}
19411949
19421950
function requestPathTypeLabel(type: string) {
1951+
if (type === 'rate_limit_action') return '封禁动作'
19431952
if (type === 'video_stream') return '视频流'
19441953
if (type === 'playback_info') return '播放信息'
19451954
if (type === 'system_info') return '系统信息'
19461955
if (type === 'base_html_player') return '播放器脚本'
19471956
return '普通代理'
19481957
}
19491958
1959+
function requestRowTitle(row: ProxyRequestDetail) {
1960+
if (row.event_type === 'block') return `封禁 ${row.playback_ip || '--'}`
1961+
if (row.event_type === 'unblock') return `解除封禁 ${row.playback_ip || '--'}`
1962+
return `${row.method} ${row.path}`
1963+
}
1964+
19501965
function requestOutcomeClass(row: ProxyRequestDetail) {
1966+
if (row.event_type === 'unblock') return 'ok'
19511967
if (row.blocked) return 'blocked'
19521968
if (row.cache_hit) return 'cache'
19531969
if (row.status_code >= 500) return 'error'
@@ -2021,6 +2037,7 @@ onBeforeUnmount(stopDashboardPolling)
20212037
{{ appInfo.version || '版本读取中' }}
20222038
<span v-if="updateChecking" class="brand-version-badge">检查中</span>
20232039
<span v-else-if="updateCheck?.has_update" class="brand-version-badge update">有更新</span>
2040+
<span v-else-if="updateCheck" class="brand-version-badge latest">最新</span>
20242041
<span v-else-if="updateCheckError" class="brand-version-badge error">失败</span>
20252042
</small>
20262043
</button>
@@ -2899,23 +2916,24 @@ onBeforeUnmount(stopDashboardPolling)
28992916
<div>
29002917
<h2>日志</h2>
29012918
<p class="muted">
2902-
单列表查看播放日志、反代请求明细和运行信息,页面打开时每 3 秒自动刷新。
2919+
单列表查看播放日志、拦截日志明细、反代请求明细和运行信息,页面打开时每 3 秒自动刷新。
29032920
</p>
29042921
</div>
29052922
<div class="panel-actions">
29062923
<span class="status-dot">{{ logsLoading ? '刷新中' : '实时刷新' }}</span>
29072924
<button class="secondary" :disabled="logsLoading" @click="refreshActivityLogs">
29082925
{{ logsLoading ? '刷新中' : '刷新' }}
29092926
</button>
2910-
<button v-if="selectedLogView !== 'proxy'" class="secondary" @click="exportLogs">导出 CSV</button>
2927+
<button v-if="!isRequestDetailLogView" class="secondary" @click="exportLogs">导出 CSV</button>
29112928
</div>
29122929
</div>
29132930
<div v-if="logsError" class="notice error">{{ logsError }}</div>
2914-
<div :class="['log-toolbar', 'compact', { proxy: selectedLogView === 'proxy' }]">
2931+
<div :class="['log-toolbar', 'compact', { proxy: isRequestDetailLogView }]">
29152932
<label>
29162933
<span>日志类型</span>
29172934
<select v-model="selectedLogView" @change="handleLogViewChange">
29182935
<option value="playback">播放日志</option>
2936+
<option value="blocked">拦截日志明细</option>
29192937
<option value="proxy">反代请求明细</option>
29202938
<option value="general">信息</option>
29212939
</select>
@@ -2937,14 +2955,15 @@ onBeforeUnmount(stopDashboardPolling)
29372955
</option>
29382956
</select>
29392957
</label>
2940-
<label v-if="selectedLogView === 'proxy'">
2958+
<label v-if="isRequestDetailLogView">
29412959
<span>请求类型</span>
29422960
<select v-model="selectedRequestPathType" @change="refreshLogsWithReset">
29432961
<option value="all">全部请求</option>
29442962
<option value="video_stream">视频流</option>
29452963
<option value="playback_info">播放信息</option>
29462964
<option value="system_info">系统信息</option>
29472965
<option value="base_html_player">播放器脚本</option>
2966+
<option v-if="selectedLogView === 'blocked'" value="rate_limit_action">封禁动作</option>
29482967
<option value="proxy">普通代理</option>
29492968
</select>
29502969
</label>
@@ -2968,7 +2987,7 @@ onBeforeUnmount(stopDashboardPolling)
29682987
<div class="log-console-meta">
29692988
<span>{{ selectedLogViewLabel }}</span>
29702989
<strong>{{ visibleLogCount }}</strong>
2971-
<span v-if="selectedLogView === 'proxy'">
2990+
<span v-if="isRequestDetailLogView">
29722991
单次最多 {{ requestDetailDisplayMax }} 条,保留 {{ requestDetailPersistDays }} 天或最近 {{ requestDetailPersistMax }} 条
29732992
</span>
29742993
<span v-else>内存最多保留最近 {{ activityLogMaxLimit }} 条可视化日志</span>
@@ -2979,7 +2998,7 @@ onBeforeUnmount(stopDashboardPolling)
29792998
class="log-console-list"
29802999
@scroll="handleScrollableLogListScroll($event, loadMoreSelectedLogs)"
29813000
>
2982-
<template v-if="selectedLogView !== 'proxy'">
3001+
<template v-if="!isRequestDetailLogView">
29833002
<article
29843003
v-for="entry in visibleActivityLogRows"
29853004
:key="`activity-${entry.id}`"
@@ -3013,17 +3032,17 @@ onBeforeUnmount(stopDashboardPolling)
30133032
<span class="request-time">{{ formatLogTime(row.timestamp_ms) }}</span>
30143033
</div>
30153034
<div class="request-path">
3016-
<strong>{{ row.method }} {{ row.path }}</strong>
3035+
<strong>{{ requestRowTitle(row) }}</strong>
30173036
<span>{{ row.detail }}</span>
30183037
</div>
30193038
</div>
30203039
<div class="request-state">
30213040
<span>{{ row.outcome }}</span>
3022-
<span>{{ row.path_type }}</span>
3023-
<span>HTTP {{ row.status_code }}</span>
3024-
<span>{{ row.duration_ms }}ms</span>
3025-
<span>{{ row.cache_hit ? '缓存命中' : '未命中' }}</span>
3026-
<span>{{ row.blocked ? '已拦截' : '未拦截' }}</span>
3041+
<span>{{ requestPathTypeLabel(row.path_type) }}</span>
3042+
<span v-if="row.event_type !== 'block' && row.event_type !== 'unblock'">HTTP {{ row.status_code }}</span>
3043+
<span v-if="row.event_type !== 'block' && row.event_type !== 'unblock'">{{ row.duration_ms }}ms</span>
3044+
<span v-if="row.event_type !== 'block' && row.event_type !== 'unblock'">{{ row.cache_hit ? '缓存命中' : '未命中' }}</span>
3045+
<span>{{ row.event_type === 'unblock' ? '已解除' : row.blocked ? '已拦截' : '未拦截' }}</span>
30273046
</div>
30283047
</article>
30293048
</template>

frontend/src/style.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ button:disabled {
228228
color: var(--warn-text);
229229
}
230230

231+
.brand-version-badge.latest {
232+
background: var(--success-bg);
233+
color: var(--success-text);
234+
}
235+
231236
.brand-version-badge.error {
232237
background: var(--danger-bg);
233238
color: var(--danger-text);

0 commit comments

Comments
 (0)