Files
dokploy/packages/server/src/utils/access-log/utils.ts
manalkaff 598fae0e92 fix: filter requests by hostname instead of path
The search filter on the Requests tab was incorrectly filtering by
RequestPath instead of RequestHost, causing "filter by name" to match
URL paths rather than hostnames. Updated the placeholder text to
reflect the correct field being searched.

Fixes #4249
2026-04-19 17:30:42 +08:00

173 lines
4.0 KiB
TypeScript

import _ from "lodash";
import type { LogEntry } from "./types";
interface HourlyData {
hour: string;
count: number;
}
export function processLogs(
logString: string,
dateRange?: { start?: string; end?: string },
): HourlyData[] {
if (_.isEmpty(logString)) {
return [];
}
const hourlyData = _(logString)
.split("\n")
.filter((line) => {
const trimmed = line.trim();
// Check if the line starts with { and ends with } to ensure it's a potential JSON object
return trimmed !== "" && trimmed.startsWith("{") && trimmed.endsWith("}");
})
.map((entry) => {
try {
const log: LogEntry = JSON.parse(entry);
if (log.ServiceName === "dokploy-service-app@file") {
return null;
}
const date = new Date(log.StartUTC);
if (dateRange?.start || dateRange?.end) {
const logDate = date.getTime();
const start = dateRange?.start
? new Date(dateRange.start).getTime()
: 0;
const end = dateRange?.end
? new Date(dateRange.end).getTime()
: Number.POSITIVE_INFINITY;
if (logDate < start || logDate > end) {
return null;
}
}
return `${date.toISOString().slice(0, 13)}:00:00Z`;
} catch (error) {
console.error("Error parsing log entry:", error);
return null;
}
})
.compact()
.countBy()
.map((count, hour) => ({ hour, count }))
.value();
return _.sortBy(hourlyData, (entry) => new Date(entry.hour).getTime());
}
interface PageInfo {
pageIndex: number;
pageSize: number;
}
interface SortInfo {
id: string;
desc: boolean;
}
export function parseRawConfig(
rawConfig: string,
page?: PageInfo,
sort?: SortInfo,
search?: string,
status?: string[],
dateRange?: { start?: string; end?: string },
): { data: LogEntry[]; totalCount: number } {
try {
if (_.isEmpty(rawConfig)) {
return { data: [], totalCount: 0 };
}
// Split logs into chunks to avoid memory issues
let parsedLogs = _(rawConfig)
.split("\n")
.filter((line) => {
const trimmed = line.trim();
return (
trimmed !== "" && trimmed.startsWith("{") && trimmed.endsWith("}")
);
})
.map((line) => {
try {
return JSON.parse(line) as LogEntry;
} catch (error) {
console.error("Error parsing log line:", error);
return null;
}
})
.compact()
.value();
// Filter out Dokploy dashboard requests
parsedLogs = parsedLogs.filter(
(log) => log.ServiceName !== "dokploy-service-app@file",
);
// Apply date range filter if provided
if (dateRange?.start || dateRange?.end) {
parsedLogs = parsedLogs.filter((log) => {
const logDate = new Date(log.StartUTC).getTime();
const start = dateRange?.start
? new Date(dateRange.start).getTime()
: 0;
const end = dateRange?.end
? new Date(dateRange.end).getTime()
: Number.POSITIVE_INFINITY;
return logDate >= start && logDate <= end;
});
}
if (search) {
parsedLogs = parsedLogs.filter((log) =>
log.RequestHost.toLowerCase().includes(search.toLowerCase()),
);
}
if (status && status.length > 0) {
parsedLogs = parsedLogs.filter((log) =>
status.some((range) => isStatusInRange(log.DownstreamStatus, range)),
);
}
const totalCount = parsedLogs.length;
if (sort) {
parsedLogs = _.orderBy(
parsedLogs,
[sort.id],
[sort.desc ? "desc" : "asc"],
);
} else {
parsedLogs = _.orderBy(parsedLogs, ["time"], ["desc"]);
}
if (page) {
const startIndex = page.pageIndex * page.pageSize;
parsedLogs = parsedLogs.slice(startIndex, startIndex + page.pageSize);
}
return { data: parsedLogs, totalCount };
} catch (error) {
console.error("Error parsing rawConfig:", error);
throw new Error("Failed to parse rawConfig");
}
}
const isStatusInRange = (status: number, range: string) => {
switch (range) {
case "info":
return status >= 100 && status <= 199;
case "success":
return status >= 200 && status <= 299;
case "redirect":
return status >= 300 && status <= 399;
case "client":
return status >= 400 && status <= 499;
case "server":
return status >= 500 && status <= 599;
default:
return false;
}
};