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:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user