fix: 修复更新日志不显示的问题 (移除 showLogs 状态改用 updateState 控制)
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
|||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
Loader2,
|
Loader2,
|
||||||
ChevronDown,
|
|
||||||
Terminal,
|
Terminal,
|
||||||
Calendar,
|
Calendar,
|
||||||
FileText,
|
FileText,
|
||||||
@@ -36,7 +35,6 @@ export default function UpdatePage() {
|
|||||||
const [checkState, setCheckState] = useState<CheckState>("idle");
|
const [checkState, setCheckState] = useState<CheckState>("idle");
|
||||||
const [updateState, setUpdateState] = useState<UpdateState>("idle");
|
const [updateState, setUpdateState] = useState<UpdateState>("idle");
|
||||||
const [logs, setLogs] = useState<string[]>([]);
|
const [logs, setLogs] = useState<string[]>([]);
|
||||||
const [showLogs, setShowLogs] = useState(false);
|
|
||||||
const [errorMsg, setErrorMsg] = useState<string>("");
|
const [errorMsg, setErrorMsg] = useState<string>("");
|
||||||
const logEndRef = useRef<HTMLDivElement>(null);
|
const logEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -70,7 +68,6 @@ export default function UpdatePage() {
|
|||||||
const handleExecuteUpdate = useCallback(async () => {
|
const handleExecuteUpdate = useCallback(async () => {
|
||||||
setUpdateState("updating");
|
setUpdateState("updating");
|
||||||
setLogs([]);
|
setLogs([]);
|
||||||
setShowLogs(true);
|
|
||||||
setErrorMsg("");
|
setErrorMsg("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -315,45 +312,35 @@ export default function UpdatePage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 更新执行日志 */}
|
{/* 更新执行日志 */}
|
||||||
{showLogs && (
|
{(updateState !== "idle" || logs.length > 0) && (
|
||||||
<div className="rounded-lg border bg-card">
|
<div className="rounded-lg border bg-card">
|
||||||
<button
|
<div className="flex w-full items-center justify-between p-4 text-sm font-medium">
|
||||||
onClick={() => setShowLogs(!showLogs)}
|
|
||||||
className="flex w-full items-center justify-between p-4 text-sm font-medium"
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Terminal className="h-4 w-4"/>
|
<Terminal className="h-4 w-4"/>
|
||||||
执行日志
|
执行日志
|
||||||
</div>
|
</div>
|
||||||
<ChevronDown
|
</div>
|
||||||
className={`h-4 w-4 transition-transform ${
|
|
||||||
showLogs ? "" : "-rotate-90"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{logs.length > 0 && (
|
<div className="border-t p-4">
|
||||||
<div className="border-t p-4">
|
<pre className="max-h-80 overflow-auto rounded-lg bg-black p-4 text-xs text-green-400 font-mono leading-relaxed">
|
||||||
<pre className="max-h-80 overflow-auto rounded-lg bg-black p-4 text-xs text-green-400 font-mono leading-relaxed">
|
{logs.map((log, i) => (
|
||||||
{logs.map((log, i) => (
|
<div key={i}>{log}</div>
|
||||||
<div key={i}>{log}</div>
|
))}
|
||||||
))}
|
{updateState === "updating" && (
|
||||||
{updateState === "updating" && (
|
<div className="flex items-center gap-1 mt-1">
|
||||||
<div className="flex items-center gap-1 mt-1">
|
<span className="animate-pulse">■</span>
|
||||||
<span className="animate-pulse">■</span>
|
执行中...
|
||||||
执行中...
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
{updateState === "done" && logs.length > 0 && (
|
||||||
{updateState === "done" && (
|
<div className="text-green-300 mt-1">✓ 更新完成</div>
|
||||||
<div className="text-green-300 mt-1">✓ 更新完成</div>
|
)}
|
||||||
)}
|
{updateState === "error" && logs.length > 0 && (
|
||||||
{updateState === "error" && (
|
<div className="text-red-400 mt-1">✗ 更新失败</div>
|
||||||
<div className="text-red-400 mt-1">✗ 更新失败</div>
|
)}
|
||||||
)}
|
<div ref={logEndRef}/>
|
||||||
<div ref={logEndRef}/>
|
</pre>
|
||||||
</pre>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -257,30 +257,30 @@ export async function executeUpdate(
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
onLog("开始更新流程...\n");
|
onLog("开始更新流程");
|
||||||
|
|
||||||
// 0. 检查 Docker 是否可用
|
// 0. 检查 Docker 是否可用
|
||||||
onLog("[0/4] 检查 Docker 环境...\n");
|
onLog("[0/4] 检查 Docker 环境");
|
||||||
const dockerCheck = checkDockerAvailable();
|
const dockerCheck = checkDockerAvailable();
|
||||||
if (!dockerCheck.ok) {
|
if (!dockerCheck.ok) {
|
||||||
onLog(`✗ ${dockerCheck.message}\n`);
|
onLog(`✗ ${dockerCheck.message}`);
|
||||||
return { success: false, message: dockerCheck.message };
|
return { success: false, message: dockerCheck.message };
|
||||||
}
|
}
|
||||||
onLog("✓ Docker 环境正常\n");
|
onLog("✓ Docker 环境正常");
|
||||||
|
|
||||||
// 1. 拉取最新代码
|
// 1. 拉取最新代码
|
||||||
const url = getAuthenticatedRemoteUrl();
|
const url = getAuthenticatedRemoteUrl();
|
||||||
onLog("[1/4] 克隆最新代码...\n");
|
onLog("[1/4] 克隆最新代码");
|
||||||
execSync(`git clone --depth 1 "${url}" "${tmpDir}"`, {
|
execSync(`git clone --depth 1 "${url}" "${tmpDir}"`, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
timeout: 120000,
|
timeout: 120000,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
onLog("✓ 代码拉取完成\n");
|
onLog("✓ 代码拉取完成");
|
||||||
|
|
||||||
// 2. 生成 .env 文件
|
// 2. 生成 .env 文件
|
||||||
onLog("[2/4] 准备环境变量...\n");
|
onLog("[2/4] 准备环境变量");
|
||||||
const envContent = [
|
const envContent = [
|
||||||
`DB_HOST=${process.env.DB_HOST || "127.0.0.1"}`,
|
`DB_HOST=${process.env.DB_HOST || "127.0.0.1"}`,
|
||||||
`DB_PORT=${process.env.DB_PORT || "3306"}`,
|
`DB_PORT=${process.env.DB_PORT || "3306"}`,
|
||||||
@@ -293,32 +293,32 @@ export async function executeUpdate(
|
|||||||
`ONEDEV_ACCESS_TOKEN=${process.env.ONEDEV_ACCESS_TOKEN || ""}`,
|
`ONEDEV_ACCESS_TOKEN=${process.env.ONEDEV_ACCESS_TOKEN || ""}`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
fs.writeFileSync(path.join(tmpDir, ".env"), envContent, "utf-8");
|
fs.writeFileSync(path.join(tmpDir, ".env"), envContent, "utf-8");
|
||||||
onLog("✓ 环境变量已准备\n");
|
onLog("✓ 环境变量已准备");
|
||||||
|
|
||||||
// 3. 构建新镜像
|
// 3. 构建新镜像
|
||||||
onLog("[3/4] 构建新 Docker 镜像...\n");
|
onLog("[3/4] 构建新 Docker 镜像");
|
||||||
execSync(`cd "${tmpDir}" && docker compose -p recipe_tool build`, {
|
execSync(`cd "${tmpDir}" && docker compose -p recipe_tool build`, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
timeout: 600000,
|
timeout: 600000,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
onLog("✓ 镜像构建完成\n");
|
onLog("✓ 镜像构建完成");
|
||||||
|
|
||||||
// 4. 重启服务
|
// 4. 重启服务
|
||||||
onLog("[4/4] 重启服务...\n");
|
onLog("[4/4] 重启服务");
|
||||||
execSync(`cd "${tmpDir}" && docker compose -p recipe_tool up -d`, {
|
execSync(`cd "${tmpDir}" && docker compose -p recipe_tool up -d`, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
onLog("✓ 服务已重启\n");
|
onLog("✓ 服务已重启");
|
||||||
|
|
||||||
return { success: true, message: "更新完成" };
|
return { success: true, message: "更新完成" };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errMsg = error instanceof Error ? error.message : "未知错误";
|
const errMsg = error instanceof Error ? error.message : "未知错误";
|
||||||
onLog(`✗ 更新失败: ${errMsg}\n`);
|
onLog(`✗ 更新失败: ${errMsg}`);
|
||||||
return { success: false, message: errMsg };
|
return { success: false, message: errMsg };
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user