mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 23:26:38 +08:00
feat(rate-limits): use 1m buckets and 24h/3d ranges
Allow overview bucket_minutes=1; rate-limit analysis uses 1-minute buckets and replaces 7d preset with 3 days.
This commit is contained in:
@@ -30,7 +30,7 @@ sidebar: false
|
|||||||
|
|
||||||
- 站点流量限制语义调整为空或 `0` 继承全局默认、`-1` 关闭、大于 `0` 自定义;修改全局默认后需发布配置版本生效。
|
- 站点流量限制语义调整为空或 `0` 继承全局默认、`-1` 关闭、大于 `0` 自定义;修改全局默认后需发布配置版本生效。
|
||||||
- Agent Docker 部署命令默认挂载命名卷 `openflare-agent-pages` 持久化 Pages 目录,重建容器时无需重新拉取静态站点包。
|
- Agent Docker 部署命令默认挂载命名卷 `openflare-agent-pages` 持久化 Pages 目录,重建容器时无需重新拉取静态站点包。
|
||||||
- 限流页新增「分析」视图:默认展示近 24 小时请求压力(RPS)与独立访客双轴趋势(5 分钟桶),支持域名过滤与 24 小时/7 天预设,并按窗口平均 RPS 排行域名与 IP;原全局默认配置迁入「配置」页签。
|
- 限流页新增「分析」视图:默认展示近 24 小时请求压力(RPS)与独立访客双轴趋势(1 分钟桶),支持域名过滤与 24 小时/3 天预设,并按窗口平均 RPS 排行域名与 IP;原全局默认配置迁入「配置」页签。
|
||||||
|
|
||||||
## [v3.4.1] - 2026-07-19
|
## [v3.4.1] - 2026-07-19
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export type SearchDraft = {
|
|||||||
|
|
||||||
export type OverviewRangeHours = 24 | 168 | 360 | 720;
|
export type OverviewRangeHours = 24 | 168 | 360 | 720;
|
||||||
|
|
||||||
/** 限流分析等短窗口场景:仅 24 小时 / 7 天 */
|
/** 限流分析等短窗口场景:仅 24 小时 / 3 天 */
|
||||||
export type RateLimitRangeHours = 24 | 168;
|
export type RateLimitRangeHours = 24 | 72;
|
||||||
|
|
||||||
export const PAGE_SIZE_OPTIONS = [20, 50, 100, 200];
|
export const PAGE_SIZE_OPTIONS = [20, 50, 100, 200];
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export const RATE_LIMIT_RANGE_OPTIONS: {
|
|||||||
label: string;
|
label: string;
|
||||||
}[] = [
|
}[] = [
|
||||||
{ value: 24, label: '24 小时' },
|
{ value: 24, label: '24 小时' },
|
||||||
{ value: 168, label: '7 天' },
|
{ value: 72, label: '3 天' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const DETAIL_SORT_OPTIONS = [
|
export const DETAIL_SORT_OPTIONS = [
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import { calculateNiceAxisMax, formatCompactNumber } from '@/lib/utils/metrics';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
formatOverviewTrendLabel,
|
formatOverviewTrendLabel,
|
||||||
type OverviewRangeHours,
|
type RateLimitRangeHours,
|
||||||
} from '../../access-logs/components/access-log-utils';
|
} from '../../access-logs/components/access-log-utils';
|
||||||
|
|
||||||
const DEFAULT_BUCKET_SECONDS = 300;
|
const DEFAULT_BUCKET_SECONDS = 60;
|
||||||
const RPS_COLOR = '#38bdf8';
|
const RPS_COLOR = '#38bdf8';
|
||||||
const VISITS_COLOR = '#a78bfa';
|
const VISITS_COLOR = '#a78bfa';
|
||||||
|
|
||||||
@@ -54,14 +54,14 @@ function formatRps(value: number) {
|
|||||||
|
|
||||||
type RatePressureChartProps = {
|
type RatePressureChartProps = {
|
||||||
data?: AccessLogOverview;
|
data?: AccessLogOverview;
|
||||||
hours: OverviewRangeHours;
|
hours: RateLimitRangeHours;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RatePressureChart({ data, hours }: RatePressureChartProps) {
|
export function RatePressureChart({ data, hours }: RatePressureChartProps) {
|
||||||
const bucketSeconds =
|
const bucketSeconds =
|
||||||
(data?.bucket_minutes && data.bucket_minutes > 0
|
(data?.bucket_minutes && data.bucket_minutes > 0
|
||||||
? data.bucket_minutes
|
? data.bucket_minutes
|
||||||
: 5) * 60 || DEFAULT_BUCKET_SECONDS;
|
: 1) * 60 || DEFAULT_BUCKET_SECONDS;
|
||||||
|
|
||||||
const rangeLabel = useMemo(() => {
|
const rangeLabel = useMemo(() => {
|
||||||
const end = data?.generated_at ? new Date(data.generated_at) : new Date();
|
const end = data?.generated_at ? new Date(data.generated_at) : new Date();
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ function RateLimitsPageContent() {
|
|||||||
const [hosts, setHosts] = useState<string[]>([]);
|
const [hosts, setHosts] = useState<string[]>([]);
|
||||||
|
|
||||||
const overviewQuery = useQuery({
|
const overviewQuery = useQuery({
|
||||||
queryKey: ['openflare', 'rate-limits', 'overview', hours, hosts, 5],
|
queryKey: ['openflare', 'rate-limits', 'overview', hours, hosts, 1],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
AccessLogService.getOverview({
|
AccessLogService.getOverview({
|
||||||
hours,
|
hours,
|
||||||
hosts: hosts.length > 0 ? hosts : undefined,
|
hosts: hosts.length > 0 ? hosts : undefined,
|
||||||
bucket_minutes: 5,
|
bucket_minutes: 1,
|
||||||
}),
|
}),
|
||||||
enabled: !!user?.is_admin && tab === 'analysis',
|
enabled: !!user?.is_admin && tab === 'analysis',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ func normalizeAccessLogOverviewQuery(input AccessLogOverviewQuery) AccessLogOver
|
|||||||
|
|
||||||
func normalizeAccessLogOverviewBucketMinutes(value int) int {
|
func normalizeAccessLogOverviewBucketMinutes(value int) int {
|
||||||
switch value {
|
switch value {
|
||||||
case 5, 60:
|
case 1, 5, 60:
|
||||||
return value
|
return value
|
||||||
default:
|
default:
|
||||||
return defaultAccessLogOverviewBucketMinutes
|
return defaultAccessLogOverviewBucketMinutes
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
// @Param host query string false "请求 Host(单域名)"
|
// @Param host query string false "请求 Host(单域名)"
|
||||||
// @Param hosts query []string false "请求 Host 列表(多域名精确匹配)"
|
// @Param hosts query []string false "请求 Host 列表(多域名精确匹配)"
|
||||||
// @Param hours query int false "统计时间范围(小时)"
|
// @Param hours query int false "统计时间范围(小时)"
|
||||||
// @Param bucket_minutes query int false "趋势桶分钟数(5 或 60,默认 60)"
|
// @Param bucket_minutes query int false "趋势桶分钟数(1、5 或 60,默认 60)"
|
||||||
// @Success 200 {object} response.Any{data=observability.AccessLogOverview} "访问日志概览"
|
// @Success 200 {object} response.Any{data=observability.AccessLogOverview} "访问日志概览"
|
||||||
// @Failure 400 {object} response.Any "参数错误"
|
// @Failure 400 {object} response.Any "参数错误"
|
||||||
// @Failure 401 {object} response.Any "未登录"
|
// @Failure 401 {object} response.Any "未登录"
|
||||||
|
|||||||
Reference in New Issue
Block a user