Skip to content

Commit 5c49ed9

Browse files
committed
fix: robust migration and UI log tweaks
- Harden migration - Improve deployment logs UI
1 parent 765203a commit 5c49ed9

3 files changed

Lines changed: 101 additions & 25 deletions

File tree

apps/api/src/db/migrate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ const addClearCacheColumn = async (db: ReturnType<typeof getDrizzle>) => {
3737
try {
3838
db.run(sql`ALTER TABLE deployments ADD COLUMN clear_cache integer NOT NULL DEFAULT 0`);
3939
console.log("[Migrate] Added clear_cache column to deployments table");
40-
} catch {
41-
// Column already exists — no-op
40+
} catch (err) {
41+
if (err instanceof Error && err.message.includes("duplicate column name")) return;
42+
console.error("[Migrate] Failed to add clear_cache column:", err);
43+
throw err;
4244
}
4345
};
4446

apps/web/src/components/project/deployments/deployment-logs.tsx

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
import React, { useState, useEffect, useRef } from "react";
1+
import React, {
2+
useState,
3+
useEffect,
4+
useRef,
5+
} from "react";
26
import { useDeploymentLogs } from "../../../hooks/useDeploymentLogs";
3-
import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card";
7+
import {
8+
Card,
9+
CardContent,
10+
CardHeader,
11+
CardTitle,
12+
} from "../../ui/card";
413
import { Terminal } from "lucide-react";
514

615
export function formatTimeAgo(dateStr: string) {
@@ -16,57 +25,101 @@ export function formatTimeAgo(dateStr: string) {
1625

1726
export function parseTimestamp(raw: string) {
1827
if (!raw) return Date.now();
19-
const normalized = raw.includes(" ") && !raw.includes("T") ? raw.replace(" ", "T") : raw;
28+
const normalized =
29+
raw.includes(" ") && !raw.includes("T")
30+
? raw.replace(" ", "T")
31+
: raw;
2032
const d = new Date(normalized);
21-
return Number.isNaN(d.getTime()) ? Date.now() : d.getTime();
33+
return Number.isNaN(d.getTime())
34+
? Date.now()
35+
: d.getTime();
2236
}
2337

24-
export function depDisplayName(projectName: string | undefined, depId: string) {
38+
export function depDisplayName(
39+
projectName: string | undefined,
40+
depId: string,
41+
) {
2542
const short = depId.slice(0, 8);
2643
if (!projectName) return short;
27-
const slug = projectName.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
44+
const slug = projectName
45+
.toLowerCase()
46+
.replace(/[^a-z0-9-]+/g, "-")
47+
.replace(/^-+|-+$/g, "")
48+
.slice(0, 63);
2849
return `${slug}-${short}`;
2950
}
3051

31-
export function DeploymentDuration({ deployment }: { deployment: any }) {
52+
export function DeploymentDuration({
53+
deployment,
54+
}: {
55+
deployment: any;
56+
}) {
3257
const [duration, setDuration] = useState("");
3358

3459
useEffect(() => {
3560
const calculate = () => {
36-
const start = parseTimestamp(deployment.createdAt);
61+
const start = parseTimestamp(
62+
deployment.createdAt,
63+
);
3764
const status = deployment.status;
38-
const isFinished = status !== "pending" && status !== "building" && status !== "deploying";
39-
const end = isFinished ? parseTimestamp(deployment.updatedAt) : Date.now();
40-
65+
const isFinished =
66+
status !== "pending" &&
67+
status !== "building" &&
68+
status !== "deploying";
69+
const end = isFinished
70+
? parseTimestamp(
71+
deployment.updatedAt,
72+
)
73+
: Date.now();
74+
4175
const diff = Math.max(0, end - start);
4276
const secs = Math.floor(diff / 1000);
4377
if (secs < 60) {
4478
setDuration(`${secs}s`);
4579
} else {
46-
const mins = Math.floor(secs / 60);
80+
const mins = Math.floor(
81+
secs / 60,
82+
);
4783
const remainingSecs = secs % 60;
48-
setDuration(`${mins}m ${remainingSecs}s`);
84+
setDuration(
85+
`${mins}m ${remainingSecs}s`,
86+
);
4987
}
5088
};
5189

5290
calculate();
53-
91+
5492
const status = deployment.status;
55-
const isFinished = status !== "pending" && status !== "building" && status !== "deploying";
93+
const isFinished =
94+
status !== "pending" &&
95+
status !== "building" &&
96+
status !== "deploying";
5697
if (isFinished) return;
5798

58-
const interval = setInterval(calculate, 1000);
99+
const interval = setInterval(
100+
calculate,
101+
1000,
102+
);
59103
return () => clearInterval(interval);
60-
}, [deployment.createdAt, deployment.updatedAt, deployment.status]);
104+
}, [
105+
deployment.createdAt,
106+
deployment.updatedAt,
107+
deployment.status,
108+
]);
61109

62-
return <span className="font-mono text-xs text-muted-foreground">{duration}</span>;
110+
return (
111+
<span className="font-mono text-xs text-muted-foreground">
112+
{duration}
113+
</span>
114+
);
63115
}
64116

65117
function fmtLogTs(raw: string | undefined) {
66118
if (!raw) return "";
67119
const d = new Date(raw);
68120
if (Number.isNaN(d.getTime())) return raw;
69-
const pad = (n: number) => String(n).padStart(2, "0");
121+
const pad = (n: number) =>
122+
String(n).padStart(2, "0");
70123
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
71124
}
72125

@@ -92,11 +145,18 @@ export function DeploymentLogs({
92145
<span className="flex items-center gap-2">
93146
<Terminal className="h-4 w-4" />
94147
Build Logs —{" "}
95-
{deployment.id.slice(0, 8)}
148+
{deployment.id.slice(
149+
0,
150+
8,
151+
)}
96152
</span>
97153
<span className="text-xs font-normal text-muted-foreground flex items-center gap-2 select-none">
98154
<span>Duration:</span>
99-
<DeploymentDuration deployment={deployment} />
155+
<DeploymentDuration
156+
deployment={
157+
deployment
158+
}
159+
/>
100160
</span>
101161
</CardTitle>
102162
</CardHeader>
@@ -115,10 +175,19 @@ export function DeploymentLogs({
115175
{logs.map((log, i) => (
116176
<div
117177
key={i}
118-
className={`log-line ${log.message.startsWith("CRITICAL") ? "dim" : ""}`}
178+
className={`log-line ${log.message.startsWith("CRITICAL") ? "" : log.message.startsWith("ERROR") ? "error" : ""}`}
119179
>
120180
<span className="log-stage">
121-
[{log.stage}]-[{fmtLogTs((log as any).timestamp || log.createdAt)}]
181+
[{log.stage}
182+
]-[
183+
{fmtLogTs(
184+
(
185+
log as any
186+
)
187+
.timestamp ||
188+
log.createdAt,
189+
)}
190+
]
122191
</span>
123192
<span className="log-msg">
124193
{log.message}

apps/web/src/index.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@
7878
opacity: 0.4;
7979
}
8080

81+
.log-line.error {
82+
color: hsl(0 72% 56%);
83+
opacity: 0.7;
84+
}
85+
8186
.log-stage {
8287
color: hsl(24 95% 53%);
8388
flex-shrink: 0;

0 commit comments

Comments
 (0)