mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: make request to dokploy server to proxy requests
This commit is contained in:
parent
74a0f5e992
commit
0c8c0844b1
@ -12,6 +12,7 @@ import { ContainerBlockChart } from "./container-block-chart";
|
||||
import { ContainerCPUChart } from "./container-cpu-chart";
|
||||
import { ContainerMemoryChart } from "./container-memory-chart";
|
||||
import { ContainerNetworkChart } from "./container-network-chart";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
const REFRESH_INTERVALS = {
|
||||
"5000": "5 Seconds",
|
||||
@ -70,84 +71,36 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
const [metrics, setMetrics] = useState<ContainerMetric>(
|
||||
{} as ContainerMetric,
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [dataPoints, setDataPoints] =
|
||||
useState<keyof typeof DATA_POINTS_OPTIONS>("50");
|
||||
const [refreshInterval, setRefreshInterval] = useState<string>("5000");
|
||||
|
||||
const fetchMetrics = async () => {
|
||||
try {
|
||||
const url = new URL(`${baseUrl}/metrics/containers`);
|
||||
|
||||
// if (dataPoints !== "all") {
|
||||
url.searchParams.append("limit", dataPoints);
|
||||
// }
|
||||
|
||||
if (!appName) {
|
||||
throw new Error(
|
||||
[
|
||||
"No Application Selected:",
|
||||
"",
|
||||
"Make Sure to select an application to monitor.",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
url.searchParams.append("appName", appName);
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error ${response.status}: ${response.statusText}. Please verify that the application "${appName}" is running and this service is included in the monitoring configuration.`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
throw new Error(
|
||||
[
|
||||
`No monitoring data available for "${appName}". This could be because:`,
|
||||
"",
|
||||
"1. The container was recently started - wait a few minutes for data to be collected",
|
||||
"2. The container is not running - verify its status",
|
||||
"3. The service is not included in your monitoring configuration",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
setHistoricalData(data);
|
||||
setMetrics(data[data.length - 1]);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: "Failed to fetch metrics, Please check your monitoring Instance is Configured correctly.",
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
error: queryError,
|
||||
} = api.admin.getContainerMetrics.useQuery(
|
||||
{
|
||||
url: baseUrl,
|
||||
token,
|
||||
dataPoints,
|
||||
appName,
|
||||
},
|
||||
{
|
||||
refetchInterval:
|
||||
dataPoints === "all" ? undefined : Number.parseInt(refreshInterval),
|
||||
enabled: !!appName,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchMetrics();
|
||||
if (!data) return;
|
||||
|
||||
if (dataPoints === "all") {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fetchMetrics();
|
||||
}, Number(refreshInterval));
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [dataPoints, appName, token, refreshInterval]);
|
||||
// @ts-ignore
|
||||
setHistoricalData(data);
|
||||
// @ts-ignore
|
||||
setMetrics(data[data.length - 1]);
|
||||
}, [data]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@ -157,7 +110,7 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (queryError) {
|
||||
return (
|
||||
<div className="mt-5 flex min-h-[55vh] w-full items-center justify-center p-4">
|
||||
<div className="max-w-xl text-center">
|
||||
@ -166,7 +119,9 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
<strong className="text-primary">{appName}</strong>
|
||||
</p>
|
||||
<p className="whitespace-pre-line text-sm text-destructive">
|
||||
{error}
|
||||
{queryError instanceof Error
|
||||
? queryError.message
|
||||
: "Failed to fetch metrics, Please check your monitoring Instance is Configured correctly."}
|
||||
</p>
|
||||
<p className=" text-sm text-muted-foreground">URL: {baseUrl}</p>
|
||||
</div>
|
||||
@ -238,11 +193,11 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
<h3 className="text-sm font-medium">Memory Usage</h3>
|
||||
</div>
|
||||
<p className="mt-2 text-2xl font-bold">
|
||||
{metrics.Memory.percentage}%
|
||||
{metrics?.Memory?.percentage}%
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{metrics.Memory.used} {metrics.Memory.unit} / {metrics.Memory.total}{" "}
|
||||
{metrics.Memory.unit}
|
||||
{metrics?.Memory?.used} {metrics?.Memory?.unit} /{" "}
|
||||
{metrics?.Memory?.total} {metrics?.Memory?.unit}
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
@ -252,8 +207,8 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
<h3 className="text-sm font-medium">Network I/O</h3>
|
||||
</div>
|
||||
<p className="mt-2 text-2xl font-bold">
|
||||
{metrics.Network.input} {metrics.Network.inputUnit} /{" "}
|
||||
{metrics.Network.output} {metrics.Network.outputUnit}
|
||||
{metrics?.Network?.input} {metrics?.Network?.inputUnit} /{" "}
|
||||
{metrics?.Network?.output} {metrics?.Network?.outputUnit}
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
@ -263,8 +218,8 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => {
|
||||
<h3 className="text-sm font-medium">Block I/O</h3>
|
||||
</div>
|
||||
<p className="mt-2 text-2xl font-bold">
|
||||
{metrics.BlockIO.read} {metrics.BlockIO.readUnit} /{" "}
|
||||
{metrics.BlockIO.write} {metrics.BlockIO.writeUnit}
|
||||
{metrics?.BlockIO?.read} {metrics?.BlockIO?.readUnit} /{" "}
|
||||
{metrics?.BlockIO?.write} {metrics?.BlockIO?.writeUnit}
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
@ -12,6 +12,7 @@ import { CPUChart } from "./cpu-chart";
|
||||
import { DiskChart } from "./disk-chart";
|
||||
import { MemoryChart } from "./memory-chart";
|
||||
import { NetworkChart } from "./network-chart";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
const REFRESH_INTERVALS = {
|
||||
"5000": "5 Seconds",
|
||||
@ -64,76 +65,56 @@ export const ShowPaidMonitoring = ({
|
||||
}: Props) => {
|
||||
const [historicalData, setHistoricalData] = useState<SystemMetrics[]>([]);
|
||||
const [metrics, setMetrics] = useState<SystemMetrics>({} as SystemMetrics);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [dataPoints, setDataPoints] =
|
||||
useState<keyof typeof DATA_POINTS_OPTIONS>("50");
|
||||
const [refreshInterval, setRefreshInterval] = useState<string>("5000");
|
||||
|
||||
const fetchMetrics = async () => {
|
||||
try {
|
||||
const url = new URL(BASE_URL);
|
||||
url.searchParams.append("limit", dataPoints);
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
error: queryError,
|
||||
} = api.admin.getServerMetrics.useQuery(
|
||||
{
|
||||
url: BASE_URL,
|
||||
token,
|
||||
dataPoints,
|
||||
},
|
||||
{
|
||||
refetchInterval:
|
||||
dataPoints === "all" ? undefined : Number.parseInt(refreshInterval),
|
||||
enabled: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error ${response.status}: ${response.statusText}. Ensure the container is running and this service is included in the monitoring configuration.`,
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
throw new Error(
|
||||
[
|
||||
"No monitoring data available. This could be because:",
|
||||
"",
|
||||
"1. You don't have setup the monitoring service, you can do in web server section.",
|
||||
"2. If you already have setup the monitoring service, wait a few minutes and refresh the page.",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
const formattedData = data.map((metric: SystemMetrics) => ({
|
||||
timestamp: metric.timestamp,
|
||||
cpu: Number.parseFloat(metric.cpu),
|
||||
cpuModel: metric.cpuModel,
|
||||
cpuCores: metric.cpuCores,
|
||||
cpuPhysicalCores: metric.cpuPhysicalCores,
|
||||
cpuSpeed: metric.cpuSpeed,
|
||||
os: metric.os,
|
||||
distro: metric.distro,
|
||||
kernel: metric.kernel,
|
||||
arch: metric.arch,
|
||||
memUsed: Number.parseFloat(metric.memUsed),
|
||||
memUsedGB: Number.parseFloat(metric.memUsedGB),
|
||||
memTotal: Number.parseFloat(metric.memTotal),
|
||||
networkIn: Number.parseFloat(metric.networkIn),
|
||||
networkOut: Number.parseFloat(metric.networkOut),
|
||||
diskUsed: Number.parseFloat(metric.diskUsed),
|
||||
totalDisk: Number.parseFloat(metric.totalDisk),
|
||||
uptime: metric.uptime,
|
||||
}));
|
||||
|
||||
const formattedData = data.map((metric: SystemMetrics) => ({
|
||||
timestamp: metric.timestamp,
|
||||
cpu: Number.parseFloat(metric.cpu),
|
||||
cpuModel: metric.cpuModel,
|
||||
cpuCores: metric.cpuCores,
|
||||
cpuPhysicalCores: metric.cpuPhysicalCores,
|
||||
cpuSpeed: metric.cpuSpeed,
|
||||
os: metric.os,
|
||||
distro: metric.distro,
|
||||
kernel: metric.kernel,
|
||||
arch: metric.arch,
|
||||
memUsed: Number.parseFloat(metric.memUsed),
|
||||
memUsedGB: Number.parseFloat(metric.memUsedGB),
|
||||
memTotal: Number.parseFloat(metric.memTotal),
|
||||
networkIn: Number.parseFloat(metric.networkIn),
|
||||
networkOut: Number.parseFloat(metric.networkOut),
|
||||
diskUsed: Number.parseFloat(metric.diskUsed),
|
||||
totalDisk: Number.parseFloat(metric.totalDisk),
|
||||
uptime: metric.uptime,
|
||||
}));
|
||||
|
||||
// @ts-ignore
|
||||
setHistoricalData(formattedData);
|
||||
// @ts-ignore
|
||||
setMetrics(formattedData[formattedData.length - 1] || {});
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: "Failed to fetch metrics, Please check your monitoring Instance is Configured correctly.",
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
setHistoricalData(formattedData);
|
||||
// @ts-ignore
|
||||
setMetrics(formattedData[formattedData.length - 1] || {});
|
||||
}, [data]);
|
||||
|
||||
const formatUptime = (seconds: number): string => {
|
||||
const days = Math.floor(seconds / (24 * 60 * 60));
|
||||
@ -143,20 +124,6 @@ export const ShowPaidMonitoring = ({
|
||||
return `${days}d ${hours}h ${minutes}m`;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchMetrics();
|
||||
|
||||
if (dataPoints === "all") {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fetchMetrics();
|
||||
}, Number(refreshInterval));
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [dataPoints, token, refreshInterval]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-[400px] w-full items-center justify-center">
|
||||
@ -165,7 +132,7 @@ export const ShowPaidMonitoring = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (queryError) {
|
||||
return (
|
||||
<div className="flex min-h-[55vh] w-full items-center justify-center p-4">
|
||||
<div className="max-w-xl text-center">
|
||||
@ -173,7 +140,9 @@ export const ShowPaidMonitoring = ({
|
||||
Error fetching metrics{" "}
|
||||
</p>
|
||||
<p className="whitespace-pre-line text-sm text-destructive">
|
||||
{error}
|
||||
{queryError instanceof Error
|
||||
? queryError.message
|
||||
: "Failed to fetch metrics, Please check your monitoring Instance is Configured correctly."}
|
||||
</p>
|
||||
<p className=" text-sm text-muted-foreground">URL: {BASE_URL}</p>
|
||||
</div>
|
||||
|
@ -399,6 +399,8 @@ export async function getServerSideProps(
|
||||
applicationId: params?.applicationId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -394,7 +394,7 @@ export async function getServerSideProps(
|
||||
await helpers.compose.one.fetch({
|
||||
composeId: params?.composeId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -343,7 +343,7 @@ export async function getServerSideProps(
|
||||
await helpers.mariadb.one.fetch({
|
||||
mariadbId: params?.mariadbId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -345,7 +345,7 @@ export async function getServerSideProps(
|
||||
await helpers.mongo.one.fetch({
|
||||
mongoId: params?.mongoId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -350,7 +350,7 @@ export async function getServerSideProps(
|
||||
await helpers.mysql.one.fetch({
|
||||
mysqlId: params?.mysqlId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -346,7 +346,7 @@ export async function getServerSideProps(
|
||||
await helpers.postgres.one.fetch({
|
||||
postgresId: params?.postgresId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -337,7 +337,7 @@ export async function getServerSideProps(
|
||||
await helpers.redis.one.fetch({
|
||||
redisId: params?.redisId,
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
protectedProcedure,
|
||||
publicProcedure,
|
||||
} from "../trpc";
|
||||
import { z } from "zod";
|
||||
|
||||
export const adminRouter = createTRPCRouter({
|
||||
one: adminProcedure.query(async ({ ctx }) => {
|
||||
@ -169,4 +170,121 @@ export const adminRouter = createTRPCRouter({
|
||||
metricsConfig: admin?.metricsConfig,
|
||||
};
|
||||
}),
|
||||
|
||||
getServerMetrics: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
token: z.string(),
|
||||
dataPoints: z.string(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
try {
|
||||
const url = new URL(input.url);
|
||||
url.searchParams.append("limit", input.dataPoints);
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${input.token}`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error ${response.status}: ${response.statusText}. Ensure the container is running and this service is included in the monitoring configuration.`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
throw new Error(
|
||||
[
|
||||
"No monitoring data available. This could be because:",
|
||||
"",
|
||||
"1. You don't have setup the monitoring service, you can do in web server section.",
|
||||
"2. If you already have setup the monitoring service, wait a few minutes and refresh the page.",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
return data as {
|
||||
cpu: string;
|
||||
cpuModel: string;
|
||||
cpuCores: number;
|
||||
cpuPhysicalCores: number;
|
||||
cpuSpeed: number;
|
||||
os: string;
|
||||
distro: string;
|
||||
kernel: string;
|
||||
arch: string;
|
||||
memUsed: string;
|
||||
memUsedGB: string;
|
||||
memTotal: string;
|
||||
uptime: number;
|
||||
diskUsed: string;
|
||||
totalDisk: string;
|
||||
networkIn: string;
|
||||
networkOut: string;
|
||||
timestamp: string;
|
||||
}[];
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
getContainerMetrics: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
token: z.string(),
|
||||
appName: z.string(),
|
||||
dataPoints: z.string(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
try {
|
||||
if (!input.appName) {
|
||||
throw new Error(
|
||||
[
|
||||
"No Application Selected:",
|
||||
"",
|
||||
"Make Sure to select an application to monitor.",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
const url = new URL(`${input.url}/metrics/containers`);
|
||||
url.searchParams.append("limit", input.dataPoints);
|
||||
url.searchParams.append("appName", input.appName);
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: {
|
||||
Authorization: `Bearer ${input.token}`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Error ${response.status}: ${response.statusText}. Please verify that the application "${input.appName}" is running and this service is included in the monitoring configuration.`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
throw new Error(
|
||||
[
|
||||
`No monitoring data available for "${input.appName}". This could be because:`,
|
||||
"",
|
||||
"1. The container was recently started - wait a few minutes for data to be collected",
|
||||
"2. The container is not running - verify its status",
|
||||
"3. The service is not included in your monitoring configuration",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
return data as {
|
||||
containerId: string;
|
||||
containerName: string;
|
||||
containerImage: string;
|
||||
containerLabels: string;
|
||||
containerCommand: string;
|
||||
containerCreated: string;
|
||||
}[];
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user