From c7c6561bc9fd40061a1008f8ead739be90ed7b76 Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 23 Jun 2025 20:59:33 +0800 Subject: [PATCH] . --- apps/blogai/components/header - Copy.tsx_ | 682 ++++++++++++++++++++++ apps/blogai/next.config.js | 3 + apps/blogai/tsconfig.json | 11 +- 3 files changed, 688 insertions(+), 8 deletions(-) create mode 100644 apps/blogai/components/header - Copy.tsx_ diff --git a/apps/blogai/components/header - Copy.tsx_ b/apps/blogai/components/header - Copy.tsx_ new file mode 100644 index 0000000..e736d99 --- /dev/null +++ b/apps/blogai/components/header - Copy.tsx_ @@ -0,0 +1,682 @@ +'use client' + +import React from 'react' + +import { Suspense } from 'react' + +import { auth } from '@/auth' +import { clearChats } from '@/app/actions' +import { Sidebar } from '@/components/sidebar' +import { SidebarList } from '@/components/sidebar-list' +import { IconSeparator } from '@/components/ui/icons' +import { SidebarFooter } from '@/components/sidebar-footer' +import { ClearHistory } from '@/components/clear-history' +import { UserMenu, UserData } from '@/components/user-menu' +import { LoginButton } from '@/components/login-button' +import { + Tooltip, + TooltipContent, + TooltipTrigger +} from '@/components/ui/tooltip' +import { Badge } from '@/components/ui/badge' +import { ConnectButton } from '@/components/connect-button' +import { SettingsDropDown } from './settings-drop-down' + + +import { useLocalStorage } from '@/lib/hooks/use-local-storage' +import { Button } from './ui/button' + +import Image from 'next/image'; +import logoImage from '@/components/images/logo.png'; +import { useRouter } from 'next/navigation' + +import { Flex, Text } from '@radix-ui/themes'; +import Link from 'next/link' +import { useTranslation } from 'react-i18next' +import { message } from 'antd' +import { LogoAI } from '@/components/chat' + +import { useState, useRef, } from "react"; + +import { Trash2, CloudDownload } from "lucide-react"; + +import { useEffect } from "react"; + +import { + BadgeInfo, + Tags, + CalendarClock, + Building2 +} from "lucide-react"; + + +export function Header() { + + const router = useRouter(); + const { t } = useTranslation(); + + const [userData, setUserData] = useLocalStorage( + 'UserData', + { + auth_token: "", + id: 1, + login_ip: "", + login_time: 0, + role: "", + user_name: "", + version: "" + } as UserData + ) + + const soonFunc = () => { + message.info(t("soon")) + } + + return ( +
+ + + + + +
+
+ {(!!userData && !!userData.user_name) ? ( +
+ +
+ + ) : ( +
+ + + + +
+ + + )} +
+
+ +
+ ) +} + +import { getRuntimeEnv } from "@/lib/ipconfig"; + +export async function getWsBase() { + let ip = await getRuntimeEnv("SUPABASE_URL"); + if (!ip) throw new Error("SUPABASE_URL 获取失败,无法构建 wsBase"); + + let wsProtocol = "ws"; + + if (typeof window !== "undefined") { + // ✅ 客户端环境(浏览器执行) + if (window.location.protocol === "https:") { + wsProtocol = "wss"; + ip = window.location.hostname; // ✅ 用 hostname 避免 HTTPS + IP 的 TLS 报错 + } + } else { + // ✅ SSR 环境,延迟导入 headers(不能放顶层) + const { headers } = await import("next/headers"); + const hdrs = headers(); + + const forwardedProto = hdrs.get("x-forwarded-proto"); + const hostHeader = hdrs.get("host"); + + if (forwardedProto === "https") { + wsProtocol = "wss"; + } + + if (hostHeader) { + // ✅ host 可能带端口,需处理 + ip = hostHeader.includes(":") ? hostHeader.split(":")[0] : hostHeader; + } + } + + const finalUrl = `${wsProtocol}://${ip}/api/v1/deploy/ws`; + console.log("✅ [WebSocket] Final URL:", finalUrl); + return finalUrl; +} + + +export function DetailPageHeader({ data }: { data: any }) { + const [loading, setLoading] = useState(false); + const [statusText, setStatusText] = useState(data?.statusText || "加载中..."); + const [progress, setProgress] = useState(data?.progress || "0%"); + const [progressBarColor, setProgressBarColor] = useState("bg-blue-500"); // ✅ 默认蓝色 + const [showProgressBar, setShowProgressBar] = useState(false); + const [showDelete, setShowDelete] = useState(false); + const [hasWSConnected, setHasWSConnected] = useState(false); + const [statusLoaded, setStatusLoaded] = useState(false); + const [canDeploy, setCanDeploy] = useState(true); + + const [currentStatus, setCurrentStatus] = useState(""); // 当前部署状态:running / stopped + const [switchLoading, setSwitchLoading] = useState(false); // 控制按钮 loading 状态 + + const [downloadPercent, setDownloadPercent] = useState("0"); // 下载百分比(0 ~ 100) + const [showDownloadBar, setShowDownloadBar] = useState(false); + + const socketRef = useRef(null); + + const { t } = useTranslation(); + + console.log("...........model_parameter =", data?.model_parameter); + + // const hasNonEmptyExtraData = data?.extra_data && typeof data.extra_data === 'object' && !Array.isArray(data.extra_data) && Object.keys(data.extra_data).length > 0 data?.model_parameter !== 0 && + // data?.model_parameter !== ""; + const hasNonEmptyExtraData = + data?.extra_data && + typeof data.extra_data === 'object' && + !Array.isArray(data.extra_data) && + Object.keys(data.extra_data).length > 0 && + data?.model_parameter !== 0 && + data?.model_parameter !== ""; + + + const initWebSocket = async (userName: string, id: number) => { + if (socketRef.current) socketRef.current.close(); + + + //const wsBase = process.env.NEXT_PUBLIC_CLIENT_BASE_WS; + const wsBase = await getWsBase(); + const socket = new WebSocket(`${wsBase}/status/${userName}/${id}`); + socketRef.current = socket; + + socket.onopen = () => console.log("WebSocket 已连接"); + + socket.onmessage = (event) => { + const msg = event.data; + console.log("收到进度信息:", msg); + setStatusText(msg); + + const percentMatch = msg.match(/(\d+)%/); + if (percentMatch && percentMatch[1]) { + setProgress(percentMatch[1] + "%"); + + if (percentMatch[1] === "100") { + console.log("部署完成 ✅,重新拉取状态!"); + fetchDeployStatus(); + } + } + + + setProgressBarColor("bg-blue-500"); // 保持默认颜色 + }; + + socket.onerror = (err) => { + console.error("WebSocket 出错:", err); + setStatusText(t("deploy.ws_error")); + }; + + socket.onclose = () => { + console.log("🔌 WebSocket 已关闭"); + setHasWSConnected(false); + socketRef.current = null; + }; + + setHasWSConnected(true); + }; + + const handleClick = async (source: "icon" | "info") => { + setLoading(true); + setStatusText(source === "icon" ? "正在处理图标操作..." : "正在处理信息操作..."); + setShowProgressBar(true) + + try { + const userData = JSON.parse(localStorage.getItem("UserData") || "null"); + if (!userData?.user_name) { + setStatusText("未登录,跳转中..."); + window.location.href = "/auth/sign-in/"; + return; + } + + const userName = userData.user_name; + const id = data?.id; + if (!id) { + setStatusText("缺少组件 ID"); + return; + } + + const res = await fetch("/api/v1/deploy/deploy", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id, user_name: userName }), + }); + + const json = await res.json(); + if (json?.header?.code === 0) { + setStatusText("部署已启动,监听中..."); + setProgressBarColor("bg-blue-500"); + initWebSocket(userName, id); + } else { + setStatusText(json.header.message || "操作失败(后端)"); + } + } catch (err) { + console.error("请求出错:", err); + setStatusText("请求失败"); + } finally { + setLoading(false); + } + }; + + const handleDelete = async () => { + if (loading) return; + + const confirmed = window.confirm(t("deploy.confirm_delete")); + if (!confirmed) return; + + setLoading(true); + setStatusText(t("deploy.deleting")); + setProgressBarColor("bg-gray-400"); + + try { + const userData = JSON.parse(localStorage.getItem("UserData") || "null"); + const userName = userData?.user_name; + const id = data?.id; + + if (!userName || !id) { + setStatusText(t("deploy.missing_user_or_id")); + return; + } + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 60_000); // ⏰ 60秒超时 + + const res = await fetch("/api/v1/deploy/delete", { + method: "POST", + headers: { "Content-Type": "application/json" }, + signal: controller.signal, + body: JSON.stringify({ user_name: userName, n_id: id }), + }); + + clearTimeout(timeoutId); // ✅ 成功返回前清除 timeout + + if (!res.ok) { + throw new Error(`HTTP 请求失败: ${res.status}`); + } + + const json = await res.json(); + if (json?.header?.code === 0) { + setStatusText(t("deploy.deletion_success")); + setProgress("0%"); + setShowDelete(false); // 删除成功后隐藏按钮 + await fetchDeployStatus(); // ✅ 删除后刷新真实状态 + } else { + setStatusText(json?.header?.message || "删除失败(后端返回错误)"); + } + } catch (err) { + if ((err as any).name === "AbortError") { + setStatusText(t("deploy.timeout")); + } else { + console.error("删除请求出错:", err); + setStatusText(t("deploy.deletion_failed_network")); + } + } finally { + setLoading(false); + } + }; + + const fetchDeployStatus = async () => { + try { + const result = await fetch("/api/v1/deploy/status", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id: data?.id }), + }).then((res) => res.json()); + + console.log("====> deploy status result:", result); + setStatusLoaded(true); + + if (!result) { + setStatusText(t("deploy.empty_response")); + setShowDelete(false); + return; + } + + const code = result?.header?.code; + const status = result?.data?.data?.status; + setCurrentStatus(status || ""); //............................................... + const userData = JSON.parse(localStorage.getItem("UserData") || "null"); + const userName = userData?.user_name; + const id = data?.id; + + console.log("🟡 状态码 code =", code, "状态 status =", status); + + if (code === 1006) { + setStatusText(t("deploy.not_deployed")); + setShowDelete(false); + setProgress("0%"); + setShowProgressBar(false); + setCanDeploy(true); // ✅ 允许部署 + return; + } + + if (status === "deploying" && userName && id && !hasWSConnected) { + setStatusText(t("deploy.connecting")); + initWebSocket(userName, id); + setShowProgressBar(true); + setCanDeploy(false); // ✅ 正在部署中,禁止点击 + } + + if (status === "running" || status === "stopped") { + console.log("✅ 允许删除(status =", status, ")"); + setShowDelete(true); + setProgress("100%"); + setShowProgressBar(true); + setCanDeploy(false); // ✅ 已部署/已停止,不允许再次 deploy + + if (status === "running") { + setStatusText(t("deploy.running")); + setProgressBarColor("bg-green-500"); + } else if (status === "stopped") { + setStatusText(t("deploy.stopped")); + setProgressBarColor("bg-gray-400"); + } + } else { + console.log("❌ 不允许删除(status =", status, ")"); + setShowDelete(false); + setProgressBarColor("bg-blue-500"); + } + } catch (err) { + console.error("获取状态失败:", err); + setStatusText(t("deploy.fetch_failed")); + setShowDelete(false); + } + }; + + + const handleSwitchStatus = async () => { + if (!data?.id) return; + + setSwitchLoading(true); + const userData = JSON.parse(localStorage.getItem("UserData") || "null"); + const userName = userData?.user_name; + + const id = data?.id; + + if (!userName || !id) { + setStatusText("缺少组件 ID 或用户信息"); + return; + } + + if (!userName) { + setStatusText("未登录,跳转中..."); + window.location.href = "/auth/sign-in/"; + return; + } + + const endpoint = currentStatus === "running" ? "/api/v1/deploy/stop" : "/api/v1/deploy/start"; + + try { + const res = await fetch(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ user_name: userName, n_id: id }), + }); + + const json = await res.json(); + if (json?.header?.code === 0) { + setStatusText(currentStatus === "running" ? "已停止" : "已启动"); + fetchDeployStatus(); // ✅ 状态切换成功后刷新 + } else { + setStatusText(json?.header?.message || "操作失败(后端)"); + } + } catch (err) { + console.error("切换请求失败:", err); + setStatusText("网络异常"); + } finally { + setSwitchLoading(false); + } + }; + + + useEffect(() => { + fetchDeployStatus(); + }, [data?.id]); + + useEffect(() => { + return () => { + if (socketRef.current) { + socketRef.current.close(); + } + }; + }, []); + + const isImagePath = + typeof data?.icon === "string" && + (data.icon.startsWith("http") || data.icon.startsWith("/")); + + const resolvedIconSrc = + isImagePath && !data.icon.startsWith("http") + ? process.env.NEXT_PUBLIC_CLIENT_IMAGE_URL + data.icon + : data.icon; + + return ( +
+
+
+ + +
+
+
+ {data?.name || "未命名组件"} +
+
+ {data?.category || "未知"} +
+
+ {data?.updated_at || "未提供"} +
+
+ {data?.company || "未知公司"} +
+
+ +
+ {hasNonEmptyExtraData && ( + + )} + + {statusLoaded && (currentStatus === "running" || currentStatus === "stopped") && ( + + )} + + {statusLoaded && showDelete && ( + + )} + + {!data?.model_parameter && ( +
+ + + + {showDownloadBar && ( +
+
+
+ )} + +
+ )} + +
+ +
+
+ + {showProgressBar && (progress !== "0%" || statusText) && ( +
+ {/* 蓝色进度条 */} +
+ + {/* 浮动在中间的文字层 */} +
+ {loading ? t("deploy.processing") : statusText} +
+
+ + )} + +
+
+ ); +} + +export function NavBack() { + + + const router = useRouter(); + const { t } = useTranslation(); + + // const query = router.query; // 假设页面 URL 是 /mypage?id=123 + + console.log("query", router) + return ( + + ) +} + +export function TimeP({ + date, +}: { + date: string +}) { + + + const router = useRouter(); + const { t } = useTranslation(); + + + return ( +

{date} {t("JellyAI")}

+ ) +} + + + + diff --git a/apps/blogai/next.config.js b/apps/blogai/next.config.js index 5a74f7e..c27017d 100644 --- a/apps/blogai/next.config.js +++ b/apps/blogai/next.config.js @@ -32,6 +32,9 @@ const nextConfig = { // Warning: This allows production builds to successfully complete even if // your project has ESLint errors. ignoreDuringBuilds: true, + }, + experimental: { + esmExternals: true, }, images: { remotePatterns: [ diff --git a/apps/blogai/tsconfig.json b/apps/blogai/tsconfig.json index 1fe8980..322920e 100644 --- a/apps/blogai/tsconfig.json +++ b/apps/blogai/tsconfig.json @@ -8,12 +8,7 @@ ], "allowJs": true, "skipLibCheck": true, - //"strict": true, - // ✅ 临时关闭严格检查 ↓↓↓ - "strict": false, - "noImplicitAny": false, - "strictNullChecks": false, - + "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "incremental": true, @@ -40,7 +35,7 @@ "name": "next" } ], - //"strictNullChecks": true + "strictNullChecks": true }, "include": [ "next-env.d.ts", @@ -48,7 +43,7 @@ "**/*.ts", "**/*.tsx", ".next/types/**/*.ts" -, "auth.ts" ], +], "exclude": [ "test", "node_modules"