From fce18c837a345343f3553d2433dfc09d7ff66f1b Mon Sep 17 00:00:00 2001 From: mapan-nju <2246839805@qq.com> Date: Fri, 17 Jan 2025 15:08:37 +0800 Subject: [PATCH] fixed some bugs --- .../[version]/dependencies/graph/page.tsx | 5 +- .../[nsbehind]/[name]/[version]/layout.tsx | 25 +++ .../[nsbehind]/[name]/[version]/page.tsx | 4 +- .../[name]/[version]/versions/page.tsx | 19 ++- .../dependencies/graphpage/route.tsx | 25 +++ .../[cratename]/[version]/versions/route.tsx | 22 +++ .../[name]/[version]/dependencies/page.tsx | 58 ------- .../[name]/[version]/dependents/page.tsx | 58 ------- .../[nsbehind]/[name]/[version]/page.tsx | 146 ------------------ app/homepage/[nsfront]/[nsbehind]/layout.tsx | 24 --- app/homepage/layout.tsx | 23 --- app/homepage/page.tsx | 110 ------------- app/homepage/search/page.tsx | 132 ---------------- app/homepage/styles.css | 5 - app/programs/[name]/[version]/page.tsx | 140 ----------------- app/programs/[name]/page.tsx | 30 ---- app/programs/cves/[id]/page.tsx | 64 -------- app/programs/layout.tsx | 20 --- app/programs/page.tsx | 97 ------------ components/CrateNav.tsx | 50 ++++++ components/DependencyGraph.tsx | 141 ++++++++--------- components/HeaderWithSearch.tsx | 59 +------ components/VersionsTable.tsx | 103 ++++++------ 23 files changed, 249 insertions(+), 1111 deletions(-) create mode 100644 app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx create mode 100644 app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/dependencies/graphpage/route.tsx create mode 100644 app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/versions/route.tsx delete mode 100644 app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx delete mode 100644 app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx delete mode 100644 app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx delete mode 100644 app/homepage/[nsfront]/[nsbehind]/layout.tsx delete mode 100644 app/homepage/layout.tsx delete mode 100644 app/homepage/page.tsx delete mode 100644 app/homepage/search/page.tsx delete mode 100644 app/homepage/styles.css delete mode 100644 app/programs/[name]/[version]/page.tsx delete mode 100644 app/programs/[name]/page.tsx delete mode 100644 app/programs/cves/[id]/page.tsx delete mode 100644 app/programs/layout.tsx delete mode 100644 app/programs/page.tsx create mode 100644 components/CrateNav.tsx diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx index 4b9e5f2..68114dc 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/dependencies/graph/page.tsx @@ -24,7 +24,7 @@ const CratePage = () => { try { setError(null); const response = await fetch( - `/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/dependencies` + `/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/dependencies/graphpage` ); if (!response.ok) { @@ -34,6 +34,7 @@ const CratePage = () => { const data = await response.json(); setResults(data); // 设置获取的数据 + console.log('resultssssssss', results); } catch (error) { console.log("Error fetching data:", error); setError("Failed to fetch data."); @@ -52,7 +53,7 @@ const CratePage = () => { - + ); diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx new file mode 100644 index 0000000..7ad3735 --- /dev/null +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx @@ -0,0 +1,25 @@ +'use client'; + +import React from 'react'; +import { useParams } from 'next/navigation'; +import CrateNav from '@/components/CrateNav'; + +export default function Layout({ + children, +}: { + children: React.ReactNode; +}) { + const params = useParams(); + + return ( +
+ + {children} +
+ ); +} \ No newline at end of file diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx index 1edaba8..3736c1f 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -30,7 +30,7 @@ const CratePage = () => { fetchCrateData(); }, [params.name, params.version, params.nsfront, params.nsbehind]); - + console.log('results in overviewwwwwwwww:', results); if (loading) return

Loading...

; if (error) return

Error: {error}

; @@ -92,7 +92,7 @@ const CratePage = () => {

In the dependencies

- {results && results.cves && results.cves.length > 0 ? ( + {results && results.dep_cves && results.dep_cves.length > 0 ? ( results.dep_cves.map((dep_cves, index) => ( <>

diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx index bfacd59..28bd868 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/versions/page.tsx @@ -3,13 +3,22 @@ import React, { useEffect, useState } from 'react'; import VersionsTable from '@/components/VersionsTable'; -import { cratesInfo } from '@/app/lib/all_interface'; +// import { VersionsInfo } from '@/app/lib/all_interface'; import { useParams } from 'next/navigation'; +interface VersionInfo { + version: string; + // publishDay: string; + dependents_number: number; +} +// interface VersionsTableProps { +// data: VersionInfo[] | undefined; + +// } const CratePage = () => { - const [results, setResults] = useState(null); + const [results, setResults] = useState(undefined); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -20,7 +29,7 @@ const CratePage = () => { useEffect(() => { const fetchCrateData = async () => { try { - const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/versions`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -40,7 +49,7 @@ const CratePage = () => { }; fetchCrateData(); // 调用函数来获取数据 }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 - + console.log('results in versionsssssss', results); if (loading) return

Loading...
; if (error) return
Error: {error}
; @@ -49,7 +58,7 @@ const CratePage = () => { return (
- +
); diff --git a/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/dependencies/graphpage/route.tsx b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/dependencies/graphpage/route.tsx new file mode 100644 index 0000000..6123492 --- /dev/null +++ b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/dependencies/graphpage/route.tsx @@ -0,0 +1,25 @@ + +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}/dependencies/graphpage`; // 替换为你的外部 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('graphhhhhhhhhh:', 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/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/versions/route.tsx b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/versions/route.tsx new file mode 100644 index 0000000..9d07059 --- /dev/null +++ b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/versions/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}/versions`; // 替换为你的外部 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/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx deleted file mode 100644 index fc966f1..0000000 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ -//Dependencies页面 -"use client"; -import React, { useEffect, useState } from 'react'; -import { useParams } from 'next/navigation' - -import DependencyTable from '../../../../../../../components/DependencyTable'; -import { dependenciesInfo } from '@/app/lib/all_interface'; - - -const CratePage = () => { - const params = useParams(); - - const [results, setResults] = useState(null); - - - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - - - useEffect(() => { - const fetchCrateData = async () => { - try { - setError(null); - const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/dependencies`); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - - - setResults(data); // 设置获取的数据 - - } catch (error) { - console.log('Error fetching data:', error); - } finally { - setLoading(false); // 完成加载 - } - }; - fetchCrateData(); // 调用函数来获取数据 - }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 - - if (loading) return
Loading...
; - if (error) return
Error: {error}
; - // console.log('dependencyyyyyyyyyyyyyyy', results?.data); - return ( - <> - {/* Existing header and search */} - - - - - ); -}; - -export default CratePage; \ No newline at end of file diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx deleted file mode 100644 index 84d0386..0000000 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ -//Dependents页面 -"use client"; -import React, { useEffect, useState } from 'react'; - -import DependentTable from '../../../../../../../components/DependentTable'; -import { dependentsInfo } from '@/app/lib/all_interface'; -import { useParams } from 'next/navigation'; - - - -const CratePage = () => { - const [results, setResults] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const params = useParams(); - - - - useEffect(() => { - const fetchCrateData = async () => { - try { - const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/dependents`); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - - - setResults(data); // 设置获取的数据 - - } catch (error) { - setError(null); - console.log('Error fetching data:', error); - } finally { - setLoading(false); // 完成加载 - } - }; - fetchCrateData(); // 调用函数来获取数据 - }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 - - if (loading) return
Loading...
; - if (error) return
Error: {error}
; - - - - return ( -
- - - -
- ); -}; - -export default CratePage; \ No newline at end of file diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx deleted file mode 100644 index c2c7df3..0000000 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ /dev/null @@ -1,146 +0,0 @@ -// Overview页面 -"use client"; -import React, { useEffect, useState } from 'react'; -import Link from 'next/link'; -import { cratesInfo } from '@/app/lib/all_interface'; -import { useParams } from 'next/navigation'; - -const CratePage = () => { - const params = useParams(); - const [results, setResults] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchCrateData = async () => { - try { - const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = await response.json(); - setResults(data); - } catch (error) { - console.error('Error fetching data:', error); - setError('An error occurred'); - } finally { - setLoading(false); - } - }; - - fetchCrateData(); - }, [params.name, params.version, params.nsfront, params.nsbehind]); - - if (loading) return

Loading...

; - if (error) return

Error: {error}

; - - return ( -
-
-
- {/* Security Advisories */} -
-

Security Advisories

-

- cve: {results && results.cves && results.cves.length > 0 && true ? JSON.stringify(results.cves) : 'No results available'} -

-
- {/* Licenses */} -
-

Licenses

-
- LICENSES: {results ? results.license : 'No results available'} -
-
- DEPENDENCY LICENSES: -
    -
  • Apache-2.0 OR MIT (116)
  • -
  • MIT (27)
  • -
  • MIT OR Uniclicense (7)
  • -
-
-
- {/* Dependencies */} -
-

Dependencies

-
- Direct: {results ? JSON.stringify(results.dependencies.direct) : 'No cves detected.'} -
-
- Indirect: {results ? JSON.stringify(results.dependencies.indirect) : 'No results available'} -
- -
View all dependencies
- -
- {/* Dependents */} -
-

Dependents

-
- Direct: {results ? JSON.stringify(results.dependents.direct) : 'No results available'} -
-
- Indirect: {results ? JSON.stringify(results.dependents.indirect) : 'No results available'} -
- -
View all dependents
- -
-
- - {/* 新增的块: doc_url 和 github_url */} -
- - - {/* OpenSSF scorecard */} -
-

OpenSSF scorecard

-

The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.

- View information about checks and how to fix failures. -
-
8.3/10
-
Scorecard as of November 11, 2024.
-
-
-
Code-Review10/10
-
Maintained10/10
-
CI/Best-Practices0/10
-
License10/10
-
Dangerous-Workflow10/10
-
Security-Policy10/10
-
Token-Permissions10/10
-
Binary-Artifacts10/10
-
Pinned-Dependencies0/10
-
-
-
-
-
- ); -}; - -export default CratePage; \ No newline at end of file diff --git a/app/homepage/[nsfront]/[nsbehind]/layout.tsx b/app/homepage/[nsfront]/[nsbehind]/layout.tsx deleted file mode 100644 index 4b5052a..0000000 --- a/app/homepage/[nsfront]/[nsbehind]/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import '@/app/ui/global.css'; -import { HeaderProvider } from '../../../context/CrateContext'; -import Header from '@/components/HeaderWithSearch'; -export const metadata = { - title: 'cratespro', - description: 'Generated by Next.js', -} - -export default function layout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - -
- {children} - - - - ) -} diff --git a/app/homepage/layout.tsx b/app/homepage/layout.tsx deleted file mode 100644 index d80a618..0000000 --- a/app/homepage/layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import '@/app/ui/global.css'; -import { HeaderProvider } from '../context/CrateContext'; - -export const metadata = { - title: 'cratespro', - description: 'Generated by Next.js', -} - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - - {children} - - - - ) -} diff --git a/app/homepage/page.tsx b/app/homepage/page.tsx deleted file mode 100644 index 90c562a..0000000 --- a/app/homepage/page.tsx +++ /dev/null @@ -1,110 +0,0 @@ -'use client'; -import React, { useState } from 'react'; -import '@/app/ui/global.css'; -import Link from 'next/link'; -import VulnerabilityList from '@/components/CveList'; -import { message } from 'antd'; - -const HomePage: React.FC = () => { - const [searchQuery, setSearchQuery] = useState(''); - const [messageApi, contextHolder] = message.useMessage();//antd-message的hooks调用 - const handleKeyPress = (e: { key: string; }) => { - // 检查是否按下了回车键 - if (e.key === 'Enter') { - // 如果是回车键,执行搜索 - performSearch(); - } - }; - - const performSearch = () => { - if (!searchQuery || searchQuery.trim() === '') { - - messageApi.warning('请输入搜索内容'); - //alert("请输入搜索内容"); // 可选:提示用户输入内容 - } - if (searchQuery.trim()) { - // 使用 Link 跳转到搜索页面 - window.location.href = `/homepage/search?crate_name=${searchQuery}`; - } - }; - - - - return ( - //绿色渐变 - <> - {contextHolder} - < div className=" min-h-screen bg-gray-900 text-white" > -
- -
open/source/insights
- - - -
- {/* 搜索部分 */} -
-

Understand your dependencies

-

Your software and your users rely not only on the code you write, but also on the code your code depends on, the code that code depends on, and so on.

-
- setSearchQuery(e.target.value)} // 更新搜索内容 - onKeyDown={handleKeyPress} - /> - {/* */} - - {/* */} -
-
- - - {/* 分割线部分 */} -
- - - - - {/* 一些介绍 */} -
-
-

New features in the deps.dev API

-

The deps.dev API, which provides free access to the data that powers this website, now has experimental batch and pull support, as well as a new version that comes with a stability guarantee and deprecation policy.

-

Learn more about the new features on our blog, or get started with the API documentation, and code examples.

-
- -
-

Seeing the big picture can be difficult—but it shouldn't be

-

The Open Source Insights page for each package shows the full dependency graph and updates it every day. The information provided can help you make informed decisions about using, building, and maintaining your software.

-

With Open Source Insights, you can actually see the dependency graph for a package, then isolate the paths to a particular dependency. Or see whether a vulnerability in a dependency might affect your code. Or compare two versions of a package to see how the dependencies have changed in a new release.

-
- -
-

How it works

-

The service repeatedly examines sites such as github.com, npmjs.com, and pkg.go.dev to find up-to-date information about open source software packages. Using that information, it builds for each package the full dependency graph from scratch—not just from package lock files—connecting it to the packages it depends on and to those that depend on it. This transitive dependency graph allows problems in any package to be made visible to the owners and users of any software they affect.

-
-
-
- - ); -} - -export default HomePage; diff --git a/app/homepage/search/page.tsx b/app/homepage/search/page.tsx deleted file mode 100644 index 0696f43..0000000 --- a/app/homepage/search/page.tsx +++ /dev/null @@ -1,132 +0,0 @@ -"use client"; -import { useEffect, useState, Suspense } from 'react'; -import { useSearchParams } from 'next/navigation'; -import Link from 'next/link'; -import NewHeader from '@/components/NewHeader'; -import { searchResult } from '@/app/lib/all_interface'; - -const Search = () => { - const [results, setResults] = useState(null); - const [currentPage, setCurrentPage] = useState(1); // 添加当前页码状态 - const [loading, setLoading] = useState(false); // 添加加载状态 - const searchParams = useSearchParams(); - const name = searchParams.get('crate_name'); - - useEffect(() => { - if (name) { - fetchResults(name, currentPage); // 使用 name 和当前页发起请求 - } - }, [name, currentPage]); // 当 name 或 currentPage 改变时重新运行 - - const fetchResults = async (query: string, page: number) => { - setLoading(true); // 开始加载数据 - try { - const response = await fetch('/api/search', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query, - pagination: { - page, // 使用传入的页码 - per_page: 20 // 每页条数 - } - }), - }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - const data1 = await response.json(); - const data = data1.data; - setResults(data); // 假设返回的数据data字段 - } catch (error) { - console.error('Error fetching data:', error); - } finally { - setLoading(false); // 数据加载完成 - } - }; - - const handleNextPage = () => { - if (results && currentPage < results.data.total_page) { - setCurrentPage(prevPage => prevPage + 1); // 增加页码 - } - }; - - const handlePreviousPage = () => { - if (currentPage > 1) { - setCurrentPage(prevPage => prevPage - 1); // 减少页码 - } - }; - - console.log("results:", results?.data.items); - return ( -
- -
-
- {loading ? ( -

Loading...

- ) : results ? ( - results.data.total_page > 0 && results.data.items.length > 0 ? ( - results.data.items.map((item, index) => ( - -
- {item.name} -
- Crate {item.version} - {item.nsfront}/{item.nsbehind} -
-
- - )) - ) : ( -

No items found.

- ) - ) : ( -

Loading...

- )} -
-
- - {/* 当前页数在按钮上方 */} -
- {results && ( -
-

- Current page: {currentPage} / Total page: {results.data.total_page} -

-
- )} - {results && results.data.total_page > 0 && ( -
- - -
- )} -
-
- ); -} - -export default function SearchPage() { - return ( - Loading...}> - - - ); -} \ No newline at end of file diff --git a/app/homepage/styles.css b/app/homepage/styles.css deleted file mode 100644 index 7a30764..0000000 --- a/app/homepage/styles.css +++ /dev/null @@ -1,5 +0,0 @@ -/* styles.css */ -.custom-margin-left { - margin-left: 100px; - /* 自定义的左边距 */ -} \ No newline at end of file diff --git a/app/programs/[name]/[version]/page.tsx b/app/programs/[name]/[version]/page.tsx deleted file mode 100644 index 03f84fb..0000000 --- a/app/programs/[name]/[version]/page.tsx +++ /dev/null @@ -1,140 +0,0 @@ -'use client'; -import { useRouter } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; -import { useParams } from 'next/navigation'; -import Header from '@/components/Header'; -import Footer from '@/components/Footer'; -import CrateInfoCard from '@/components/CrateInfoCard'; -import DependenciesList, { Dependency } from '@/components/DependenciesList'; -import DependencyGraph from '@/components/DependencyGraph'; -import VulnerabilitiesList, { Vulnerability } from '@/components/VulnerabilitiesList'; -import SecurityAdvisories from '@/components/SecurityAdvisories'; -import BenchmarkResults from '@/components/BenchmarkResults'; -import VersionsSelector from '@/components/VersionsSelector'; -import { CrateInfo } from '@/app/lib/crate_info'; -//异步获取依赖树 -async function fetchDependencyTree(name: string, version: string) { - const response = await fetch(`/api/crates/${name}/${version}`); - const versionData = await response.json(); - - const dependencies = versionData.dependencies || []; - const dependenciesDetails = await Promise.all(dependencies.map(async (subDep: { name: string; version: string; }) => { - return fetchDependencyTree(subDep.name, subDep.version); - })); - - return { - name, - version, - dependencies: dependenciesDetails - }; -} - - - -const CratePage = () => { - const router = useRouter(); - const [crateInfo, setCrateInfo] = useState(null); - const [versions, setVersions] = useState([]); - const [dependencies, setDependencies] = useState([]); - // const [graphDependencies, setGraphDependencies] = useState(); - const [vulnerabilities, setVulnerabilities] = useState([]); - const [benchmarks, setBenchmarks] = useState<{ name: string; value: string }[]>([]); - const { name, version } = useParams<{ name: string; version: string }>(); - - - const crateName = Array.isArray(name) ? name[0] : name; - const currentVersion = Array.isArray(version) ? version[0] : version; - - - useEffect(() => { - - fetch(`/api/crates/${crateName}`) - .then(response => response.json()) - .then(data => { - setCrateInfo(data.crateInfo || {}); - setVersions(data.versions || []); - - setBenchmarks(data.benchmarks || []); - }) - .catch(error => { - console.error('Error fetching data:', error); - }); - - }, [crateName, currentVersion]); - - useEffect(() => { - fetch(`/api/crates/${name}/${version}`) - .then(response => response.json()) - .then(data => { - setVulnerabilities(data.vulnerabilities); - setDependencies(data.dependencies); - }) - .catch(error => { - console.error('Error fetching data:', error); - }); - - - }, [crateName, currentVersion, name, version]); - - - useEffect(() => { - async function loadDependencies() { - try { - const graphDep = await fetchDependencyTree(crateName, currentVersion); - console.log(graphDep); - // setGraphDependencies(graphDep); - } catch (error) { - console.error('Error fetching dependency tree:', error); - } - } - - if (crateName && currentVersion) { - loadDependencies(); - } - }, [crateName, currentVersion]); - - const handleVersionChange = (version: string) => { - console.log(`Navigating to /programs/${name}/${version}`); - router.push(`/programs/${name}/${version}`); - }; - - const handleDependencyClick = (dependencyName: string, dependencyVersion: string) => { - router.push(`/programs/${dependencyName}/${dependencyVersion}`); - }; - - if (!crateInfo) { - return
Loading...
; - } - - return ( -
-
window.history.back()} /> -
-
-
- - - - -
-
- - - - -
-
-
-
-
- ); -}; - -export default CratePage; diff --git a/app/programs/[name]/page.tsx b/app/programs/[name]/page.tsx deleted file mode 100644 index 6a6ccfa..0000000 --- a/app/programs/[name]/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; -import { useRouter } from 'next/navigation'; -import React, { useEffect } from 'react'; -import { useParams } from 'next/navigation'; - -const CratePage = () => { - const router = useRouter(); - const { name } = useParams(); - - useEffect(() => { - if (name) { - fetch(`/api/crates/${name}`) - .then(response => response.json()) - .then(data => { - const versions = data.versions || []; - if (versions.length > 0) { - const latestVersion = versions[versions.length - 1]; - router.replace(`/programs/${name}/${latestVersion}`); - } - }) - .catch(error => { - console.error('Error fetching data:', error); - }); - } - }, [name, router]); - - return
Loading...
; -}; - -export default CratePage; diff --git a/app/programs/cves/[id]/page.tsx b/app/programs/cves/[id]/page.tsx deleted file mode 100644 index fd884c0..0000000 --- a/app/programs/cves/[id]/page.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client'; -import React, { useEffect, useState } from 'react'; -import CveInfoCard from '@/components/CveInfoCard'; -import Footer from '@/components/Footer'; -import Header from '@/components/Header'; - -interface CVE { - id: string; - description: string; - name: string; - version: string; - sourceUrl: string; - startVersion: string; - endVersion: string; - versions: string[]; -} - -async function fetchCVE(id: string): Promise { - // Simulate fetching CVE data - return { - id, - description: `Description for ${id}`, - name: `crate-${Math.floor(Math.random() * 100)}`, - version: `v${Math.floor(Math.random() * 5)}.${Math.floor(Math.random() * 10)}.${Math.floor(Math.random() * 10)}`, - sourceUrl: 'https://example.com', - startVersion: 'v1.0.0', - endVersion: 'v2.0.0', - versions: ['v1.0.0', 'v1.2.0', 'v1.3.0', 'v2.0.0'], // Example versions - }; -} - -type Params = Promise<{ id: string }> - -export default function CVEDetail({ params }: { params: Params }) { - const { id } = React.use(params); - - const [cve, setCve] = useState(null); - - useEffect(() => { - const getCVE = async () => { - const data = await fetchCVE(id); - setCve(data); - }; - getCVE(); - }, [id]); - - if (!cve) { - return
Loading...
; - } - - return ( -
-
window.history.back()} /> -
-
-
{/* Adjust width as needed */} - -
-
-
-
-
- ); -} diff --git a/app/programs/layout.tsx b/app/programs/layout.tsx deleted file mode 100644 index a146b7b..0000000 --- a/app/programs/layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import '@/app/ui/global.css'; -import { inter } from '@/app/ui/fonts'; -import SideNav from '@/app/ui/programs/sidenav'; - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - - -
-
- -
-
- {children} -
-
- - - ); -} diff --git a/app/programs/page.tsx b/app/programs/page.tsx deleted file mode 100644 index 9f3e03e..0000000 --- a/app/programs/page.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client'; -import { useEffect, useState } from 'react'; -import Link from 'next/link'; - -//接口定义-Rust程序的结构 -interface ObjectData { - name: string; - description: string; - category: string; -} - -//接口定义-描述CVE数据的结构 -interface CVE { - id: string; - description: string; - name: string; - version: string; -} - -const ProgramsPage = () => { - const [objects, setObjects] = useState([]); //从接口获取Rust程序数据 - const [cveData, setCveData] = useState([]); //从接口获取CVE数据 - - //获取数据 - useEffect(() => { - fetch('/api/crates') //这里应调用后端接口:GET http://localhost:6888/api/crates (?) - .then(response => response.json()) - .then(data => { - console.log('Fetched crates data:', data); //调试输出 - if (Array.isArray(data)) { - setObjects(data); - } else { - console.error('Crates data is not an array:', data); - setObjects([]);//如果不是数组,设置为空数组 - } - }); - - fetch('/api/cves') //这里应调用后端接口: GET http://localhost:6888/api/crates/{name} (?) - .then(response => { - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - return response.text(); // 暂时使用 text() 来调试 - }) - .then(text => { - console.log('Response text:', text); - return JSON.parse(text); // 手动解析 JSON - }) - .then(data => setCveData(data)) - .catch(error => console.error('Fetch error:', error)); - }, []); - - const categories = [...new Set(objects.map(obj => obj.category))]; - - return ( -
-
-
-

All Rust Programs

- {categories.map((category) => ( -
-

{category}

-
    - {objects - .filter(obj => obj.category === category) - .map((obj) => ( -
  • - - {obj.name} - -

    {obj.description}

    -
  • - ))} -
-
- ))} -
- -
-

CVE List

-
    - {cveData.map((cve) => ( -
  • {/* 这里transform hover:scale-105和submit弹窗有冲突 */} - - {cve.id} - -

    {cve.description}

    -
  • - ))} -
-
-
-
- ); -}; - -export default ProgramsPage; diff --git a/components/CrateNav.tsx b/components/CrateNav.tsx new file mode 100644 index 0000000..13101cc --- /dev/null +++ b/components/CrateNav.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +interface CrateNavProps { + nsfront: string; + nsbehind: string; + name: string; + version: string; +} + +const CrateNav: React.FC = ({ nsfront, nsbehind, name, version }) => { + const pathname = usePathname(); + const basePath = `/${nsfront}/${nsbehind}/${name}/${version}`; + + const navItems = [ + { name: 'Overview', path: '' }, + { name: 'Dependencies', path: '/dependencies' }, + { name: 'Dependents', path: '/dependents' }, + { name: 'Versions', path: '/versions' }, + ]; + + const isActive = (path: string) => { + if (path === '') { + return pathname === basePath; + } + return pathname === `${basePath}${path}`; + }; + + return ( + + ); +}; + +export default CrateNav; \ No newline at end of file diff --git a/components/DependencyGraph.tsx b/components/DependencyGraph.tsx index a265e06..0a9cfec 100644 --- a/components/DependencyGraph.tsx +++ b/components/DependencyGraph.tsx @@ -1,10 +1,11 @@ import React, { useEffect, useRef, useState } from 'react'; import * as d3 from 'd3'; +import { useParams } from "next/navigation"; export interface GraphDependency { - crate_name: string; - version: string; - dependencies?: GraphDependency[]; + name_and_version: string; + cve_count: number; + direct_dependency?: GraphDependency[]; } interface DependencyNode extends d3.SimulationNodeDatum { @@ -17,55 +18,36 @@ interface DependencyLink extends d3.SimulationLinkDatum { target: DependencyNode; } -export interface DependencyGraphProps { - crateName: string; - currentVersion: string; -} - -const DependencyGraph: React.FC = ({ crateName, currentVersion }) => { +const DependencyGraph: React.FC = () => { const [graphDependencies, setGraphDependencies] = useState(null); const d3Container = useRef(null); + const params = useParams(); useEffect(() => { - async function fetchDependencyTree(name: string, version: string, visited: Set): Promise { - const nodeId = `${name}@${version}`; - if (visited.has(nodeId)) { - return { crate_name: name, version, dependencies: [] }; - } - visited.add(nodeId); - - const url = `/api/graph/${name}/${version}/direct`; - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); + const fetchDependencyGraph = async () => { + try { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/dependencies/graphpage`); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data: GraphDependency = await response.json(); + console.log('data in graphhhhhhhhhhhhh', data); + setGraphDependencies(data); + } catch (error) { + console.error('Error fetching dependency graph:', error); } + }; - const dependencies = await response.json(); - const dependenciesDetails = await Promise.all(dependencies.map(async (dep: { name: string; version: string; }) => { - return fetchDependencyTree(dep.name, dep.version, visited); - })); - - return { - crate_name: name, - version, - dependencies: dependenciesDetails - }; - } - - async function loadDependencies() { - const visited = new Set(); - const graphDep = await fetchDependencyTree(crateName, currentVersion, visited); - setGraphDependencies(graphDep); - } - - loadDependencies(); - }, [crateName, currentVersion]); + fetchDependencyGraph(); + }, [params.nsfront, params.nsbehind, params.name, params.version]); useEffect(() => { if (!graphDependencies || d3Container.current === null) return; - const width = 800; - const height = 600; + const containerWidth = Math.max(d3Container.current.clientWidth, 1200); + const containerHeight = Math.max(d3Container.current.clientHeight, 800); + const width = containerWidth * 1.5; + const height = containerHeight * 1.5; d3.select(d3Container.current).select('svg').remove(); @@ -78,11 +60,11 @@ const DependencyGraph: React.FC = ({ crateName, currentVer svg.append('defs').append('marker') .attr('id', 'arrowhead') .attr('viewBox', '0 -5 10 10') - .attr('refX', 20) + .attr('refX', 17) .attr('refY', 0) .attr('orient', 'auto') - .attr('markerWidth', 7) - .attr('markerHeight', 7) + .attr('markerWidth', 3) + .attr('markerHeight', 3) .append('path') .attr('d', 'M 0,-5 L 10,0 L 0,5') .attr('fill', '#333') @@ -92,17 +74,17 @@ const DependencyGraph: React.FC = ({ crateName, currentVer const links: DependencyLink[] = []; function processDependencies(dep: GraphDependency, parent?: DependencyNode) { - const nodeId = `${dep.crate_name}@${dep.version}`; + const nodeId = `${dep.name_and_version}`; let node = nodesMap.get(nodeId); if (!node) { - node = { id: nodeId, color: parent ? '#69b3a2' : 'red' }; + node = { id: nodeId, color: parent ? '#5c6470' : '#32e0c4' }; nodesMap.set(nodeId, node); } if (parent) { links.push({ source: parent, target: node }); } - if (dep.dependencies) { - dep.dependencies.forEach(subDep => processDependencies(subDep, node)); + if (dep.direct_dependency) { + dep.direct_dependency.forEach(subDep => processDependencies(subDep, node)); } } @@ -110,26 +92,27 @@ const DependencyGraph: React.FC = ({ crateName, currentVer const nodes = Array.from(nodesMap.values()); - // Remove the forceCenter to allow free movement of nodes const simulation = d3.forceSimulation(nodes) - .force('link', d3.forceLink(links).id(d => d.id).distance(100)) - .force('charge', d3.forceManyBody().strength(-500)) - // Remove center force to prevent auto centering - .force('collide', d3.forceCollide().radius(50)); + .force('link', d3.forceLink(links).id(d => d.id).distance(80)) + .force('charge', d3.forceManyBody().strength(-300)) + .force('center', d3.forceCenter(width / 2, height / 2)) + .force('collide', d3.forceCollide().radius(10)); + + const g = svg.append('g'); - const link = svg.append('g') + const link = g.append('g') .selectAll('line') .data(links) .enter().append('line') - .attr('stroke-width', 2) + .attr('stroke-width', 1.5) .attr('stroke', '#333') .attr('marker-end', 'url(#arrowhead)'); - const node = svg.append('g') + const node = g.append('g') .selectAll('circle') .data(nodes) .enter().append('circle') - .attr('r', 15) + .attr('r', 5) .attr('fill', d => d.color) .attr('stroke', '#333') .attr('stroke-width', 1.5) @@ -141,7 +124,7 @@ const DependencyGraph: React.FC = ({ crateName, currentVer node.append('title') .text(d => d.id); - const labels = svg.append('g') + const labels = g.append('g') .attr('class', 'labels') .selectAll('text') .data(nodes) @@ -165,8 +148,8 @@ const DependencyGraph: React.FC = ({ crateName, currentVer .attr('y2', d => (d.target as DependencyNode).y!); node - .attr('cx', d => d.x = Math.max(10, Math.min(width - 10, d.x!))) - .attr('cy', d => d.y = Math.max(10, Math.min(height - 10, d.y!))); + .attr('cx', d => d.x!) + .attr('cy', d => d.y!); labels .attr('x', d => d.x! + 10) @@ -190,33 +173,31 @@ const DependencyGraph: React.FC = ({ crateName, currentVer d.fy = null; } - // Zoom functionality - // const zoom = d3.zoom() - // .scaleExtent([0.1, 10]) // Minimum and maximum zoom scales - // .on('zoom', (event) => { - // svg.selectAll('g') - // .attr('transform', event.transform); - // }); - - // svg.call(zoom as any); - - // }, [graphDependencies]); const zoom = d3.zoom() - .scaleExtent([0.1, 10]) // Minimum and maximum zoom scales + .scaleExtent([0.1, 20]) .on('zoom', (event) => { - svg.selectAll('g') - .attr('transform', event.transform); + g.attr('transform', event.transform); }); - svg.call(zoom); // 不再使用 any + svg.call(zoom); + const initialScale = 0.6; + svg.call( + zoom.transform, + d3.zoomIdentity + .translate(width / 2, height / 2) + .scale(initialScale) + .translate(-width / 2, -height / 2) + ); }, [graphDependencies]); return ( -
-
-
+
); }; -export default DependencyGraph; +export default DependencyGraph; \ No newline at end of file diff --git a/components/HeaderWithSearch.tsx b/components/HeaderWithSearch.tsx index b5f56d5..75c08d3 100644 --- a/components/HeaderWithSearch.tsx +++ b/components/HeaderWithSearch.tsx @@ -6,6 +6,7 @@ import { useHeaderContext } from '../app/context/CrateContext'; import { useParams } from 'next/navigation'; import { message } from 'antd'; import { useRouter } from 'next/navigation'; +// import { cratesInfo } from '@/app/lib/all_interface'; const Header = () => { const router = useRouter(); @@ -19,17 +20,6 @@ const Header = () => { const [currentVersion, setCurrentVersion] = useState(params.version); // 存储当前选中的版本 - const [activeTab, setActiveTab] = useState('overview'); // 默认选中 Overview - // console.log('pathhhhhhhhhhhhhhhhhhhhhhhhhhhh:', params); - // 定义导航项数据 - const navItems = [ - { name: 'Overview', path: '' }, - { name: 'Dependencies', path: '/dependencies' }, - { name: 'Dependents', path: '/dependents' }, - { name: 'Versions', path: '/versions' }, - ]; - - const handleKeyPress = (e: { key: string; }) => { // 检查是否按下了回车键 if (e.key === 'Enter') { @@ -52,20 +42,6 @@ const Header = () => { useEffect(() => { const path = params.path || ''; // 确保有值 console.log('path:', path); - switch (path) { - case '': - setActiveTab('overview'); - break; - case 'dependencies': - setActiveTab('dependencies'); - break; - case 'dependents': - setActiveTab('dependents'); - break; - default: - setActiveTab('overview'); - break; - } }, [params.path]); // 依赖于 params.path // 使用 useEffect 从 API 获取数据 useEffect(() => { @@ -142,7 +118,6 @@ const Header = () => { key={index} onClick={() => { setCurrentVersion(version); - setActiveTab('overview'); // 更新导航条为 Overview }} href={`/${params.nsfront}/${params.nsbehind}/${crateData.results?.crate_name}/${version}`} > @@ -166,43 +141,15 @@ const Header = () => { placeholder="Search for open source crates" className="p-2 border-none rounded-md text-gray-800 w-80 max-w-2xl" value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} // 更新搜索内容 + onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={handleKeyPress} /> - {/* */} - - {/* */}
- - diff --git a/components/VersionsTable.tsx b/components/VersionsTable.tsx index b48af74..0addd27 100644 --- a/components/VersionsTable.tsx +++ b/components/VersionsTable.tsx @@ -1,77 +1,59 @@ 'use client'; -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useEffect, useState } from 'react'; import { Table } from 'antd'; import { useParams } from 'next/navigation'; // 假设后端接口返回的类型 interface VersionInfo { version: string; - publishDay: string; - dependentsNumber: number; + dependents: number; // 保持原始字段以便从API获取 } -interface VersionsTableProps { - data: string[] | undefined; // 传入的版本号数组 +// 新增 PublishDay 接口 +interface FormattedVersionInfo extends VersionInfo { + publishDay: string; // 新增字段 } -const VersionsTable: React.FC = ({ data }) => { - const [versionsData, setVersionsData] = useState([]); - const [loading, setLoading] = useState(false); +const VersionsTable: React.FC = () => { + const [versionsData, setVersionsData] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); const params = useParams(); - // 获取版本发布日的函数 - const fetchPublishDay = useCallback(async (version: string) => { - try { - const response = await fetch(`/api/publish-day/${version}`); - if (!response.ok) { - throw new Error('Failed to fetch publish day'); - } - const data = await response.json(); - return data.publishDay || 'N/A'; // 如果没有返回值,则默认返回 'N/A' - } catch (error) { - console.error('Error fetching publish day:', error); - return 'N/A'; // 请求失败时返回默认值 - } - }, []); + useEffect(() => { + const fetchVersionsData = async () => { + try { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/versions`); - // 获取版本依赖数的函数 - const fetchDependentsNumber = useCallback(async (version: string) => { - try { - const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${version}/dependents`); - if (!response.ok) { - throw new Error('Failed to fetch dependents number'); - } - const data = await response.json(); - return data.direct_count + data.indirect_count || 0; // 如果没有返回值,则默认返回 0 - } catch (error) { - console.error('Error fetching dependents number:', error); - return 0; // 请求失败时返回默认值 - } - }, [params.nsfront, params.nsbehind, params.name]); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } - // 请求版本的发布日和依赖数 - const fetchVersionDetails = useCallback(async (version: string) => { - const publishDay = await fetchPublishDay(version); - const dependentsNumber = await fetchDependentsNumber(version); - return { version, publishDay, dependentsNumber }; - }, [fetchPublishDay, fetchDependentsNumber]); + const data: VersionInfo[] = await response.json(); - useEffect(() => { - const fetchData = async () => { - if (data && data.length > 0) { - setLoading(true); - const versionDetails = await Promise.all( - data.map(async (version) => { - return await fetchVersionDetails(version); - }) - ); - setVersionsData(versionDetails); - setLoading(false); + // 检查数据是否有效 + if (!Array.isArray(data)) { + throw new Error('Invalid data format'); + } + + // 将 API 数据转换为 Table 需要的格式 + const formattedData = data.map((item) => ({ + version: item.version, + dependents: item.dependents, // 保留依赖数 + publishDay: 'N/A', // 设置默认发布日为 N/A + })); + + setVersionsData(formattedData); // 设置获取的数据 + } catch (error) { + setError(error instanceof Error ? error.message : 'An unknown error occurred'); // 改进错误处理 + console.error('Error fetching data:', error); + } finally { + setLoading(false); // 完成加载 } }; - fetchData(); - }, [data, fetchVersionDetails]); + fetchVersionsData(); // 调用函数来获取数据 + }, [params.nsfront, params.nsbehind, params.name, params.version]); // 依赖项数组 const columns = [ { @@ -82,18 +64,21 @@ const VersionsTable: React.FC = ({ data }) => { }, { title: 'Publish Day', - dataIndex: 'publishDay', - key: 'publishDay', + dataIndex: 'publishDay', // 修改为使用 publishDay + key: 'publishDay', // 修改为使用 publishDay render: (text: string) => {text}, }, { title: 'Dependents Number', - dataIndex: 'dependentsNumber', - key: 'dependentsNumber', + dataIndex: 'dependents', + key: 'dependents', render: (text: number) => {text}, }, ]; + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + return (