fix: channel affinity (#2799)

* fix: channel affinity log styles

* fix: Issue with incorrect data storage when switching key sources

* feat: support not retrying after a single rule configuration fails

* fix: render channel affinity tooltip as multiline content

* feat: channel affinity cache hit

* fix: prevent ChannelAffinityUsageCacheModal infinite loading and hide data before fetch

* chore: format backend with gofmt and frontend with prettier/eslint autofix
This commit is contained in:
Seefs
2026-02-02 14:37:31 +08:00
committed by GitHub
parent 80a609b7c6
commit f244a9e661
61 changed files with 2012 additions and 1004 deletions

View File

@@ -236,9 +236,7 @@ async function prepareOAuthState(options = {}) {
if (shouldLogout) {
try {
await API.get('/api/user/logout', { skipErrorHandler: true });
} catch (err) {
}
} catch (err) {}
localStorage.removeItem('user');
updateAPI();
}

View File

@@ -261,7 +261,7 @@ export const processRawData = (
};
// 检查数据是否跨年
const showYear = isDataCrossYear(data.map(item => item.created_at));
const showYear = isDataCrossYear(data.map((item) => item.created_at));
data.forEach((item) => {
result.uniqueModels.add(item.model_name);
@@ -269,7 +269,11 @@ export const processRawData = (
result.totalQuota += item.quota;
result.totalTimes += item.count;
const timeKey = timestamp2string1(item.created_at, dataExportDefaultTime, showYear);
const timeKey = timestamp2string1(
item.created_at,
dataExportDefaultTime,
showYear,
);
if (!result.timePoints.includes(timeKey)) {
result.timePoints.push(timeKey);
}
@@ -328,10 +332,14 @@ export const aggregateDataByTimeAndModel = (data, dataExportDefaultTime) => {
const aggregatedData = new Map();
// 检查数据是否跨年
const showYear = isDataCrossYear(data.map(item => item.created_at));
const showYear = isDataCrossYear(data.map((item) => item.created_at));
data.forEach((item) => {
const timeKey = timestamp2string1(item.created_at, dataExportDefaultTime, showYear);
const timeKey = timestamp2string1(
item.created_at,
dataExportDefaultTime,
showYear,
);
const modelKey = item.model_name;
const key = `${timeKey}-${modelKey}`;
@@ -372,7 +380,7 @@ export const generateChartTimePoints = (
);
const showYear = isDataCrossYear(generatedTimestamps);
chartTimePoints = generatedTimestamps.map(ts =>
chartTimePoints = generatedTimestamps.map((ts) =>
timestamp2string1(ts, dataExportDefaultTime, showYear),
);
}

View File

@@ -1,3 +1,21 @@
/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
export function parseHttpStatusCodeRules(input) {
const raw = (input ?? '').toString().trim();
if (raw.length === 0) {
@@ -35,7 +53,9 @@ export function parseHttpStatusCodeRules(input) {
}
const merged = mergeRanges(ranges);
const tokens = merged.map((r) => (r.start === r.end ? `${r.start}` : `${r.start}-${r.end}`));
const tokens = merged.map((r) =>
r.start === r.end ? `${r.start}` : `${r.start}-${r.end}`,
);
const normalized = tokens.join(',');
return {
@@ -78,7 +98,9 @@ function isNumber(s) {
function mergeRanges(ranges) {
if (!Array.isArray(ranges) || ranges.length === 0) return [];
const sorted = [...ranges].sort((a, b) => (a.start !== b.start ? a.start - b.start : a.end - b.end));
const sorted = [...ranges].sort((a, b) =>
a.start !== b.start ? a.start - b.start : a.end - b.end,
);
const merged = [sorted[0]];
for (let i = 1; i < sorted.length; i += 1) {

View File

@@ -217,7 +217,11 @@ export function timestamp2string(timestamp) {
);
}
export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour', showYear = false) {
export function timestamp2string1(
timestamp,
dataExportDefaultTime = 'hour',
showYear = false,
) {
let date = new Date(timestamp * 1000);
let year = date.getFullYear();
let month = (date.getMonth() + 1).toString();
@@ -248,7 +252,9 @@ export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour', sho
nextDay = '0' + nextDay;
}
// 周视图结束日期也仅在跨年时显示年份
let nextStr = showYear ? nextWeekYear + '-' + nextMonth + '-' + nextDay : nextMonth + '-' + nextDay;
let nextStr = showYear
? nextWeekYear + '-' + nextMonth + '-' + nextDay
: nextMonth + '-' + nextDay;
str += ' - ' + nextStr;
}
return str;
@@ -257,7 +263,9 @@ export function timestamp2string1(timestamp, dataExportDefaultTime = 'hour', sho
// 检查时间戳数组是否跨年
export function isDataCrossYear(timestamps) {
if (!timestamps || timestamps.length === 0) return false;
const years = new Set(timestamps.map(ts => new Date(ts * 1000).getFullYear()));
const years = new Set(
timestamps.map((ts) => new Date(ts * 1000).getFullYear()),
);
return years.size > 1;
}