diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx index cb83328..dbe0d19 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx @@ -2,6 +2,8 @@ "use client"; import React, { useEffect, useState } from "react"; import { useParams } from "next/navigation"; +import Image from "next/image"; +import Link from "next/link"; // import DependencyTable from "@/components/DependencyTable"; import DependencyGraph from "@/components/DependencyGraph"; // 假设你已经创建了 DependencyGraph 组件 @@ -9,6 +11,7 @@ import { dependenciesInfo } from "@/app/lib/all_interface"; const CratePage = () => { const params = useParams(); + // const router = useRouter(); // const currentVersion = params.version; // const crateName = params.name; const [results, setResults] = useState(null); @@ -32,29 +35,77 @@ const CratePage = () => { } const data = await response.json(); - - setResults(data); // 设置获取的数据 - console.log('resultssssssss', results); + setResults(data); } catch (error) { console.log("Error fetching data:", error); setError("Failed to fetch data."); } finally { - setLoading(false); // 完成加载 + setLoading(false); } }; - fetchCrateData(); // 调用函数来获取数据 - },); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 + fetchCrateData(); + }, [params.nsfront, params.nsbehind, params.name, params.version]); // 只保留必要的依赖项 - if (loading) return
Loading...
; - if (error) return
Error: {error}
; + if (loading) return
Loading...
; + if (error) return
Error: {error}
; return ( -
+
+ {/* 图表区域 */} +
+ +
+ + {/* 页脚 */} +
+
+
+
+
+ CratesPro Logo +
+
+

Resources

+
    +
  • Documentation
  • +
  • About
  • +
  • Blog
  • +
  • FAQ
  • +
+
+
+

API

+
    +
  • API
  • +
  • BigQuery Dataset
  • +
  • GitHub
  • +
+
- +
+

Legal

+
    +
  • Legal
  • +
  • Privacy
  • +
  • Terms
  • +
+
+
+
+

Copyright © 2023 jp21.com.cn All Rights Reserved(@ICPBH180237号)

+
+
+
+
); }; diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx index 87dc666..3234d3c 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx @@ -1,7 +1,7 @@ 'use client'; import React from 'react'; -import { useParams } from 'next/navigation'; +import { useParams, usePathname } from 'next/navigation'; import CrateNav from '@/components/CrateNav'; export default function Layout({ @@ -10,15 +10,22 @@ export default function Layout({ children: React.ReactNode; }) { const params = useParams(); + const pathname = usePathname(); + + // 判断当前页面是否为SenseLeak页面 + const isSenseLeakPage = pathname.includes('/senseleak'); return (
- + {/* 仅在非SenseLeak页面显示导航栏 */} + {!isSenseLeakPage && ( + + )} {children}
); diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx index 499775c..c23a130 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -155,6 +155,7 @@ const CratePage = () => { const [packageCurrentPage, setPackageCurrentPage] = useState(1); const [depCurrentPage, setDepCurrentPage] = useState(1); const itemsPerPage = 1; + const basePath = `/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`; useEffect(() => { const fetchCrateData = async () => { @@ -502,23 +503,25 @@ const CratePage = () => { {/* 右侧内容区域 - 占据1列 */}
- {/* 第一个摘要 */} + {/* Documentation */}
icon

- 摘要 + Documentation

-
-

Documentation URL

+
+

+ Documentation URL: +

{
- {/* 第二个摘要 */} + {/* GitHub Links */}
icon

- 摘要 + GitHub Links

- - {/* Documentation */} - - - {/* GitHub Links */} + {/* 第一个摘要 */} - {/* OpenSSF Scorecard */}
diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/senseleak/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/senseleak/page.tsx new file mode 100644 index 0000000..1ba8b4f --- /dev/null +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/senseleak/page.tsx @@ -0,0 +1,267 @@ +'use client'; +import React, { useEffect, useState, useRef } from 'react'; +import Link from 'next/link'; +import { useParams } from 'next/navigation'; +import Image from 'next/image'; + +interface SenseLeakRes { + exist: boolean; + res: string; +} + +const SenseLeakPage = () => { + const params = useParams(); + const [versionsList, setVersionsList] = useState([]); + const [senseLeakData, setSenseLeakData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + + // 添加滚动条状态和引用 + const [isScrolling, setIsScrolling] = useState(false); + const scrollTimeoutRef = useRef(); + const containerRef = useRef(null); + + // 添加滚动位置状态 + const [scrollPosition, setScrollPosition] = useState(0); + + // 获取版本列表和SenseLeak数据 + useEffect(() => { + const fetchData = async () => { + try { + // 获取版本列表 + const versionsResponse = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + if (!versionsResponse.ok) { + throw new Error(`HTTP error! status: ${versionsResponse.status}`); + } + const versionsData = await versionsResponse.json(); + setVersionsList(versionsData.versions || []); + + // 获取 SenseLeak 数据 + const senseLeakResponse = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/senseleak`); + if (!senseLeakResponse.ok) { + throw new Error(`HTTP error! status: ${senseLeakResponse.status}`); + } + const senseLeakData = await senseLeakResponse.json(); + setSenseLeakData(senseLeakData); + console.log('senseLeakData!!!!!!!!!!!!!!!!!!!!', senseLeakData); + + setLoading(false); + } catch (error) { + console.error('Error fetching data:', error); + setError('获取数据时出错'); + setLoading(false); + } + }; + + fetchData(); + }, [params.nsfront, params.nsbehind, params.name, params.version]); + + // 当前选中的版本 + const currentVersion = params.version as string; + + // 添加过滤后的版本列表计算 + const filteredVersions = versionsList.filter(version => + version.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + // 处理滚动事件 + const handleScroll = (e: React.UIEvent) => { + // 立即更新滚动位置 + setScrollPosition(e.currentTarget.scrollTop); + setIsScrolling(true); + + // 清除之前的定时器 + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current); + } + + // 设置新的定时器,1.5秒后隐藏滚动条 + scrollTimeoutRef.current = setTimeout(() => { + setIsScrolling(false); + }, 1500); + }; + + // 处理搜索输入 + const handleSearch = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value); + // 搜索时重置滚动位置 + if (containerRef.current) { + containerRef.current.scrollTop = 0; + setScrollPosition(0); + } + }; + + // 清理定时器 + useEffect(() => { + return () => { + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current); + } + }; + }, []); + + if (loading) return ( +
+

加载中...

+
+ ); + + if (error) return ( +
+

错误: {error}

+
+ ); + + return ( +
+ {/* 左侧边栏 - 版本列表 */} +
+ {/* Logo 和搜索框区域 - 固定不动 */} +
+ {/* Logo */} +
+ CratesPro Logo +
+ + {/* 搜索框 */} +
+
+ Search Icon +
+ +
+
+ + {/* 版本列表区域 - 可滚动 */} +
+ {/* 自定义滚动条 */} +
+ +
+
+ {filteredVersions.map((version, index) => ( + +
+
+
+ Version Icon +
+ Version-{version} +
+
+ + ))} + {filteredVersions.length === 0 && ( +
+ 没有找到匹配的版本 +
+ )} +
+
+
+
+ + {/* 右侧内容区域 - SenseLeak数据 */} +
+
+
+
+

+ SenseLeak Analysis: {params.name}/{params.version} +

+
+ +
+ {senseLeakData ? ( + senseLeakData.exist ? ( +
+                                    {senseLeakData.res}
+                                
+ ) : ( +
+ No data +

+ no senseleak detected +

+
+ ) + ) : ( +
+ No data +

+ 加载中... +

+
+ )} +
+
+
+
+ ); +}; + +export default SenseLeakPage; diff --git a/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/senseleak/route.tsx b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/senseleak/route.tsx new file mode 100644 index 0000000..83dba4b --- /dev/null +++ b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/senseleak/route.tsx @@ -0,0 +1,22 @@ +import { NextRequest, NextResponse } from "next/server"; +type Params = Promise<{ nsfront: string, nsbehind: string, cratename: string, version: string }> +export async function GET(req: NextRequest, props: { params: Params }) { + try { + const params = await props.params + const { nsfront, nsbehind, cratename, version } = params; + const endpoint = process.env.CRATES_PRO_INTERNAL_HOST; + + const externalApiUrl = `${endpoint}/api/crates/${nsfront}/${nsbehind}/${cratename}/${version}/senseleak`; // 替换为你的外部 API URL + const externalRes = await fetch(externalApiUrl); + if (!externalRes.ok) { + throw new Error('Failed to fetch external data'); + } + const externalData = await externalRes.json(); + console.log('External API Response:', externalData); + return NextResponse.json(externalData); + } catch (error) { + console.error('Error:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + + } +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 2b7c1bc..8d2db70 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,6 +7,7 @@ import Image from 'next/image'; import SignIn from '@/components/sign-in'; import { SessionProvider } from "next-auth/react" + const HomePage: React.FC = () => { const [searchQuery, setSearchQuery] = useState(''); const [messageApi, contextHolder] = message.useMessage(); @@ -16,7 +17,7 @@ const HomePage: React.FC = () => { { title: "AI-Powered Multi-Dimensional Analysis", description: "CratesPro leverages advanced AI models to provide in-depth, multi-dimensional analysis of Rust crates. By analyzing a variety of data points—including code structure, dependencies, and historical vulnerability patterns—the platform offers intelligent recommendations to optimize crates, enhance security, and reduce the risk of bugs or inefficiencies in your code.", - icon: "/images/homepage/ai-powered.png" + icon: "/images/homepage/homepage-1.png" }, { title: "Vulnerability Detection and Risk Assessment", @@ -27,9 +28,22 @@ const HomePage: React.FC = () => { title: "CVE Tracking and Vulnerability Propagation", description: "CratesPro tracks CVE (Common Vulnerabilities and Exposures) related to Rust crates and their dependencies. The platform monitors crate versions and their updates, correlating them with known CVE databases to provide real-time updates on newly discovered vulnerabilities. This helps developers stay informed about potential threats to their projects and take proactive measures.", icon: "/images/homepage/cve-tracking.png" + }, + { + title: "User-Uploaded Crates for Comprehensive Analysis", + description: "CratesPro supports users who wish to upload their custom crates for comprehensive analysis. After uploading, CratesPro performs a full evaluation of the crate, analyzing dependencies, potential vulnerabilities, and even compatibility with other crates in the Rust ecosystem. This feature empowers developers to ensure the security and reliability of their own creations before publication.", + icon: "/images/homepage/homepage-4.png" + }, + { + title: "Dependency Tracking and Visualization", + description: "CratesPro offers detailed tracking of crate dependencies, visualizing the relationships between crates and how vulnerabilities propagate across them. By modeling dependencies and interactions, developers can see how a vulnerability in one crate may affect others in the ecosystem, offering a clearer understanding of potential risks.", + icon: "/images/homepage/homepage-5.png" } ]; + const itemsToShow = 3; // 一次显示3个卡片 + const maxSlideIndex = features.length - itemsToShow; // 最大可滑动索引 + const handleKeyPress = (e: { key: string; }) => { if (e.key === 'Enter') { performSearch(); @@ -46,106 +60,98 @@ const HomePage: React.FC = () => { }; const nextSlide = () => { - setActiveSlide((prev) => (prev === features.length - 1 ? 0 : prev + 1)); + setActiveSlide((prev) => (prev >= maxSlideIndex ? maxSlideIndex : prev + 1)); }; const prevSlide = () => { - setActiveSlide((prev) => (prev === 0 ? features.length - 1 : prev - 1)); + setActiveSlide((prev) => (prev <= 0 ? 0 : prev - 1)); }; return ( <> {contextHolder}
- {/* 导航栏 */} -
- - -
- - - -
-
- - {/* 英雄区 */} -
-
+ {/* 修改整个上半部分的容器结构 */} +
+ {/* 背景图片容器 - 减小高度 */} +
CratesPro Logo
-

- CratesPro - Rust Crate Analysis and Recommendation Platform -

-

- CratesPro is a sophisticated platform designed to analyze and evaluate Rust crates. -

- {/* 搜索框 */} -
- setSearchQuery(e.target.value)} - onKeyDown={handleKeyPress} - /> - -
-
+ {/* 内容容器 - 对应减小高度 */} +
+ {/* 导航栏 - 重新设计布局 */} +
+ {/* Logo 移到左上角 */} +
+ CratesPro Logo +
- {/* 彩色分隔线 */} -
- - - + {/* 导航链接居中 */} + + + {/* 登录按钮靠右 */} +
+ + + +
+
+ + {/* 英雄区 - 调整内容和间距 */} +
+

+ CratesPro +

+

+ Rust Crate Analysis and Recommendation Platform +

+

+ CratesPro is a sophisticated platform designed to analyze and evaluate Rust crates. +

+ + {/* 搜索框 */} +
+ setSearchQuery(e.target.value)} + onKeyDown={handleKeyPress} + /> + +
+
+ +
+
- {/* 为什么选择我们 */} -
-

+ {/* Why Choose Us 部分 - 增加上下内边距 */} +
{/* 增加上下内边距到 py-24 */} +

{/* 增加标题下方间距 */} Why Choose Us

@@ -153,8 +159,9 @@ const HomePage: React.FC = () => { {/* 左箭头 */} {/* 轮播内容 */} -
-
+
+
{features.map((feature, index) => ( -
+
+ {/* 图片容器 - 绝对定位 */} +
+ {feature.title} +
+ + {/* 卡片主体 */}
-
- {feature.title} -
-

+ h-[430px] + rounded-[46px] + bg-gradient-to-b from-[#CED4FF] to-[#F6FCFF] + shadow-[0_0_20px_0_rgba(43,88,221,0.16)] + p-8 + flex + flex-col + items-center + mx-auto + pt-20 {/* 调整上内边距 */} + "> +

{feature.title}

-

+

{feature.description}

@@ -203,12 +217,17 @@ const HomePage: React.FC = () => { {/* 右箭头 */}
diff --git a/app/ui/global.css b/app/ui/global.css index 22eaf6d..392b94d 100644 --- a/app/ui/global.css +++ b/app/ui/global.css @@ -27,4 +27,29 @@ input[type='number']::-webkit-outer-spin-button { src: url('/fonts/HarmonyOS_Sans_SC_Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; -} \ No newline at end of file +} + +/* +.custom-scrollbar::-webkit-scrollbar { + width: 4px; +} + +.custom-scrollbar::-webkit-scrollbar-track { + background: transparent; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: #4B68FF; + border-radius: 3px; + height: 4px; +} + +.custom-scrollbar { + scrollbar-width: thin; + scrollbar-color: #4B68FF transparent; +} + +.custom-scrollbar::-webkit-scrollbar-thumb:vertical { + min-height: 4px; + max-height: 4px; +} */ \ No newline at end of file diff --git a/components/CrateNav.tsx b/components/CrateNav.tsx index cc06285..62e659f 100644 --- a/components/CrateNav.tsx +++ b/components/CrateNav.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import Link from 'next/link'; -import { usePathname } from 'next/navigation'; +import { usePathname, useRouter } from 'next/navigation'; import NewHeader from '@/components/NewHeader'; import { useHeaderContext } from '../app/context/CrateContext'; import Image from 'next/image'; @@ -14,6 +14,7 @@ interface CrateNavProps { const CrateNav: React.FC = ({ nsfront, nsbehind, name, version }) => { const pathname = usePathname(); + const router = useRouter(); const basePath = `/${nsfront}/${nsbehind}/${name}/${version}`; const [isOpen, setIsOpen] = useState(false); const [currentVersion, setCurrentVersion] = useState(version); @@ -47,6 +48,9 @@ const CrateNav: React.FC = ({ nsfront, nsbehind, name, version }) if (path === '') { return pathname === basePath; } + if (path === '/dependencies') { + return pathname === `${basePath}${path}` || pathname === `${basePath}${path}/graph`; + } return pathname === `${basePath}${path}`; }; @@ -58,6 +62,9 @@ const CrateNav: React.FC = ({ nsfront, nsbehind, name, version }) setIsOpen(false); }; + // 判断是否在 graph page + const isGraphPage = pathname.includes('/dependencies/graph'); + return (
@@ -120,23 +127,37 @@ const CrateNav: React.FC = ({ nsfront, nsbehind, name, version })
- + + Show List + + + )} +
diff --git a/public/images/homepage/ai-powered.png b/public/images/homepage/homepage-1.png similarity index 100% rename from public/images/homepage/ai-powered.png rename to public/images/homepage/homepage-1.png diff --git a/public/images/homepage/vulnerability-detection.png b/public/images/homepage/homepage-2.png similarity index 100% rename from public/images/homepage/vulnerability-detection.png rename to public/images/homepage/homepage-2.png diff --git a/public/images/homepage/cve-tracking.png b/public/images/homepage/homepage-3.png similarity index 100% rename from public/images/homepage/cve-tracking.png rename to public/images/homepage/homepage-3.png diff --git a/public/images/homepage/homepage-4.png b/public/images/homepage/homepage-4.png new file mode 100644 index 0000000..b364d00 Binary files /dev/null and b/public/images/homepage/homepage-4.png differ diff --git a/public/images/homepage/homepage-5.png b/public/images/homepage/homepage-5.png new file mode 100644 index 0000000..f3b2b07 Binary files /dev/null and b/public/images/homepage/homepage-5.png differ diff --git a/public/images/homepage/homepage-bg.png b/public/images/homepage/homepage-bg.png new file mode 100644 index 0000000..b1b0258 Binary files /dev/null and b/public/images/homepage/homepage-bg.png differ diff --git a/public/images/homepage/senseleak-file.png b/public/images/homepage/senseleak-file.png new file mode 100644 index 0000000..dc222d6 Binary files /dev/null and b/public/images/homepage/senseleak-file.png differ diff --git a/public/images/homepage/senseleak-search.png b/public/images/homepage/senseleak-search.png new file mode 100644 index 0000000..efce185 Binary files /dev/null and b/public/images/homepage/senseleak-search.png differ