feat: add remote logs error when is not reachable

This commit is contained in:
Mauricio Siu
2025-01-23 00:44:31 -06:00
parent 6edd2a81e5
commit 03e1c17675
12 changed files with 4450 additions and 24 deletions

View File

@@ -17,8 +17,15 @@ interface Props {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
serverId?: string; serverId?: string;
errorMessage?: string;
} }
export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { export const ShowDeployment = ({
logPath,
open,
onClose,
serverId,
errorMessage,
}: Props) => {
const [data, setData] = useState(""); const [data, setData] = useState("");
const [showExtraLogs, setShowExtraLogs] = useState(false); const [showExtraLogs, setShowExtraLogs] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
@@ -99,6 +106,8 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
} }
}, [filteredLogs, autoScroll]); }, [filteredLogs, autoScroll]);
const optionalErrors = parseLogs(errorMessage || "");
return ( return (
<Dialog <Dialog
open={open} open={open}
@@ -157,9 +166,17 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
<TerminalLine key={index} log={log} noTimestamp /> <TerminalLine key={index} log={log} noTimestamp />
)) ))
) : ( ) : (
<div className="flex justify-center items-center h-full text-muted-foreground"> <>
<Loader2 className="h-6 w-6 animate-spin" /> {optionalErrors.length > 0 ? (
</div> optionalErrors.map((log: LogLine, index: number) => (
<TerminalLine key={`extra-${index}`} log={log} noTimestamp />
))
) : (
<div className="flex justify-center items-center h-full text-muted-foreground">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
)}
</>
)} )}
</div> </div>
</DialogContent> </DialogContent>

View File

@@ -8,7 +8,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { api } from "@/utils/api"; import { api, type RouterOutputs } from "@/utils/api";
import { RocketIcon } from "lucide-react"; import { RocketIcon } from "lucide-react";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { CancelQueues } from "./cancel-queues"; import { CancelQueues } from "./cancel-queues";
@@ -18,8 +18,11 @@ import { ShowDeployment } from "./show-deployment";
interface Props { interface Props {
applicationId: string; applicationId: string;
} }
export const ShowDeployments = ({ applicationId }: Props) => { export const ShowDeployments = ({ applicationId }: Props) => {
const [activeLog, setActiveLog] = useState<string | null>(null); const [activeLog, setActiveLog] = useState<
RouterOutputs["deployment"]["all"][number] | null
>(null);
const { data } = api.application.one.useQuery({ applicationId }); const { data } = api.application.one.useQuery({ applicationId });
const { data: deployments } = api.deployment.all.useQuery( const { data: deployments } = api.deployment.all.useQuery(
{ applicationId }, { applicationId },
@@ -100,7 +103,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
<Button <Button
onClick={() => { onClick={() => {
setActiveLog(deployment.logPath); setActiveLog(deployment);
}} }}
> >
View View
@@ -112,9 +115,10 @@ export const ShowDeployments = ({ applicationId }: Props) => {
)} )}
<ShowDeployment <ShowDeployment
serverId={data?.serverId || ""} serverId={data?.serverId || ""}
open={activeLog !== null} open={Boolean(activeLog && activeLog.logPath !== null)}
onClose={() => setActiveLog(null)} onClose={() => setActiveLog(null)}
logPath={activeLog} logPath={activeLog?.logPath || ""}
errorMessage={activeLog?.errorMessage || ""}
/> />
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -26,7 +26,9 @@ export const ShowPreviewBuilds = ({
serverId, serverId,
trigger, trigger,
}: Props) => { }: Props) => {
const [activeLog, setActiveLog] = useState<string | null>(null); const [activeLog, setActiveLog] = useState<
RouterOutputs["deployment"]["all"][number] | null
>(null);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
return ( return (
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
@@ -77,7 +79,7 @@ export const ShowPreviewBuilds = ({
<Button <Button
onClick={() => { onClick={() => {
setActiveLog(deployment.logPath); setActiveLog(deployment);
}} }}
> >
View View
@@ -89,9 +91,10 @@ export const ShowPreviewBuilds = ({
</DialogContent> </DialogContent>
<ShowDeployment <ShowDeployment
serverId={serverId || ""} serverId={serverId || ""}
open={activeLog !== null} open={Boolean(activeLog && activeLog.logPath !== null)}
onClose={() => setActiveLog(null)} onClose={() => setActiveLog(null)}
logPath={activeLog} logPath={activeLog?.logPath || ""}
errorMessage={activeLog?.errorMessage || ""}
/> />
</Dialog> </Dialog>
); );

View File

@@ -17,12 +17,14 @@ interface Props {
serverId?: string; serverId?: string;
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
errorMessage?: string;
} }
export const ShowDeploymentCompose = ({ export const ShowDeploymentCompose = ({
logPath, logPath,
open, open,
onClose, onClose,
serverId, serverId,
errorMessage,
}: Props) => { }: Props) => {
const [data, setData] = useState(""); const [data, setData] = useState("");
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
@@ -105,6 +107,8 @@ export const ShowDeploymentCompose = ({
} }
}, [filteredLogs, autoScroll]); }, [filteredLogs, autoScroll]);
const optionalErrors = parseLogs(errorMessage || "");
return ( return (
<Dialog <Dialog
open={open} open={open}
@@ -161,9 +165,17 @@ export const ShowDeploymentCompose = ({
<TerminalLine key={index} log={log} noTimestamp /> <TerminalLine key={index} log={log} noTimestamp />
)) ))
) : ( ) : (
<div className="flex justify-center items-center h-full text-muted-foreground"> <>
<Loader2 className="h-6 w-6 animate-spin" /> {optionalErrors.length > 0 ? (
</div> optionalErrors.map((log: LogLine, index: number) => (
<TerminalLine key={`extra-${index}`} log={log} noTimestamp />
))
) : (
<div className="flex justify-center items-center h-full text-muted-foreground">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
)}
</>
)} )}
</div> </div>
</DialogContent> </DialogContent>

View File

@@ -8,7 +8,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { api } from "@/utils/api"; import { api, type RouterOutputs } from "@/utils/api";
import { RocketIcon } from "lucide-react"; import { RocketIcon } from "lucide-react";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { CancelQueuesCompose } from "./cancel-queues-compose"; import { CancelQueuesCompose } from "./cancel-queues-compose";
@@ -19,7 +19,9 @@ interface Props {
composeId: string; composeId: string;
} }
export const ShowDeploymentsCompose = ({ composeId }: Props) => { export const ShowDeploymentsCompose = ({ composeId }: Props) => {
const [activeLog, setActiveLog] = useState<string | null>(null); const [activeLog, setActiveLog] = useState<
RouterOutputs["deployment"]["all"][number] | null
>(null);
const { data } = api.compose.one.useQuery({ composeId }); const { data } = api.compose.one.useQuery({ composeId });
const { data: deployments } = api.deployment.allByCompose.useQuery( const { data: deployments } = api.deployment.allByCompose.useQuery(
{ composeId }, { composeId },
@@ -100,7 +102,7 @@ export const ShowDeploymentsCompose = ({ composeId }: Props) => {
<Button <Button
onClick={() => { onClick={() => {
setActiveLog(deployment.logPath); setActiveLog(deployment);
}} }}
> >
View View
@@ -112,9 +114,10 @@ export const ShowDeploymentsCompose = ({ composeId }: Props) => {
)} )}
<ShowDeploymentCompose <ShowDeploymentCompose
serverId={data?.serverId || ""} serverId={data?.serverId || ""}
open={activeLog !== null} open={Boolean(activeLog && activeLog.logPath !== null)}
onClose={() => setActiveLog(null)} onClose={() => setActiveLog(null)}
logPath={activeLog} logPath={activeLog?.logPath || ""}
errorMessage={activeLog?.errorMessage || ""}
/> />
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -226,7 +226,11 @@ export const AddTemplate = ({ projectId }: Props) => {
<ScrollArea className="h-[calc(98vh-8rem)]"> <ScrollArea className="h-[calc(98vh-8rem)]">
<div className="p-6"> <div className="p-6">
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>} {isError && (
<AlertBlock type="error" className="mb-4">
{error?.message}
</AlertBlock>
)}
{templates.length === 0 ? ( {templates.length === 0 ? (
<div className="flex justify-center items-center w-full gap-2 min-h-[50vh]"> <div className="flex justify-center items-center w-full gap-2 min-h-[50vh]">

View File

@@ -0,0 +1 @@
ALTER TABLE "deployment" ADD COLUMN "errorMessage" text;

File diff suppressed because it is too large Load Diff

View File

@@ -407,6 +407,13 @@
"when": 1737306063563, "when": 1737306063563,
"tag": "0057_tricky_living_tribunal", "tag": "0057_tricky_living_tribunal",
"breakpoints": true "breakpoints": true
},
{
"idx": 58,
"version": "6",
"when": 1737612903012,
"tag": "0058_brown_sharon_carter",
"breakpoints": true
} }
] ]
} }

View File

@@ -47,6 +47,7 @@ export const deployments = pgTable("deployment", {
createdAt: text("createdAt") createdAt: text("createdAt")
.notNull() .notNull()
.$defaultFn(() => new Date().toISOString()), .$defaultFn(() => new Date().toISOString()),
errorMessage: text("errorMessage"),
}); });
export const deploymentsRelations = relations(deployments, ({ one }) => ({ export const deploymentsRelations = relations(deployments, ({ one }) => ({

View File

@@ -98,6 +98,17 @@ export const createDeployment = async (
} }
return deploymentCreate[0]; return deploymentCreate[0];
} catch (error) { } catch (error) {
await db
.insert(deployments)
.values({
applicationId: deployment.applicationId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updateApplicationStatus(application.applicationId, "error"); await updateApplicationStatus(application.applicationId, "error");
console.log(error); console.log(error);
throw new TRPCError({ throw new TRPCError({
@@ -164,6 +175,17 @@ export const createDeploymentPreview = async (
} }
return deploymentCreate[0]; return deploymentCreate[0];
} catch (error) { } catch (error) {
await db
.insert(deployments)
.values({
previewDeploymentId: deployment.previewDeploymentId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updatePreviewDeployment(deployment.previewDeploymentId, { await updatePreviewDeployment(deployment.previewDeploymentId, {
previewStatus: "error", previewStatus: "error",
}); });
@@ -226,6 +248,17 @@ echo "Initializing deployment" >> ${logFilePath};
} }
return deploymentCreate[0]; return deploymentCreate[0];
} catch (error) { } catch (error) {
await db
.insert(deployments)
.values({
composeId: deployment.composeId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updateCompose(compose.composeId, { await updateCompose(compose.composeId, {
composeStatus: "error", composeStatus: "error",
}); });

View File

@@ -64,7 +64,7 @@ export const createMount = async (input: typeof apiCreateMount._type) => {
console.log(error); console.log(error);
throw new TRPCError({ throw new TRPCError({
code: "BAD_REQUEST", code: "BAD_REQUEST",
message: "Error creating the mount", message: `Error ${error instanceof Error ? error.message : error}`,
cause: error, cause: error,
}); });
} }
@@ -91,7 +91,7 @@ export const createFileMount = async (mountId: string) => {
console.log(`Error creating the file mount: ${error}`); console.log(`Error creating the file mount: ${error}`);
throw new TRPCError({ throw new TRPCError({
code: "BAD_REQUEST", code: "BAD_REQUEST",
message: "Error creating the mount", message: `Error creating the mount ${error instanceof Error ? error.message : error}`,
cause: error, cause: error,
}); });
} }