diff --git a/app/api/crates/[cratename]/[version]/dependencies/route.tsx b/app/api/crates/[cratename]/[version]/dependencies/route.tsx new file mode 100644 index 0000000..b1484af --- /dev/null +++ b/app/api/crates/[cratename]/[version]/dependencies/route.tsx @@ -0,0 +1,22 @@ +type Params = Promise<{ cratename: string, version: string }> +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(req: NextRequest, props: { params: Params }) { + try { + const params = await props.params + const { cratename, version } = params; + + const externalApiUrl = `http://210.28.134.203:6888/api/crates/${cratename}/${version}/dependencies`; // 替换为你的外部 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/api/crates/[cratename]/[version]/dependents/route.tsx b/app/api/crates/[cratename]/[version]/dependents/route.tsx new file mode 100644 index 0000000..ed2ef81 --- /dev/null +++ b/app/api/crates/[cratename]/[version]/dependents/route.tsx @@ -0,0 +1,20 @@ +import { NextRequest, NextResponse } from "next/server"; +type Params = Promise<{ cratename: string, version: string }> +export async function GET(req: NextRequest, props: { params: Params }) { + try { + const params = await props.params + const { cratename, version } = params; + const externalApiUrl = `http://210.28.134.203:6888/api/crates/${cratename}/${version}/dependents`; // 替换为你的外部 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/api/crates/[cratename]/[version]/route.tsx b/app/api/crates/[cratename]/[version]/route.tsx new file mode 100644 index 0000000..b450bdd --- /dev/null +++ b/app/api/crates/[cratename]/[version]/route.tsx @@ -0,0 +1,22 @@ +import { NextRequest, NextResponse } from "next/server"; +type Params = Promise<{ cratename: string, version: string }> + +export async function GET(req: NextRequest, props: { params: Params }) { + try { + const params = await props.params + const { cratename, version } = params; + + const externalApiUrl = `http://210.28.134.203:6888/api/crates/${cratename}/${version}`; // 替换为你的外部 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('crateinfo 11111111', externalData); + return NextResponse.json(externalData); + } catch (error) { + console.error('Error:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + + } +} diff --git a/app/api/crates/[cratename]/route.tsx b/app/api/crates/[cratename]/route.tsx new file mode 100644 index 0000000..ed2896d --- /dev/null +++ b/app/api/crates/[cratename]/route.tsx @@ -0,0 +1,22 @@ +import { NextRequest, NextResponse } from 'next/server'; +type Params = Promise<{ cratename: string, version: string }> +export async function GET(req: NextRequest, props: { params: Params }) { + try { + const params = await props.params + const { cratename, version } = params; + console.log('cratename', cratename, 'version', version); + const externalApiUrl = `http://210.28.134.203:6888/api/crates/${cratename}`; // 替换为你的外部 API URL + const externalRes = await fetch(externalApiUrl); + if (!externalRes.ok) { + throw new Error('Failed to fetch external data'); + } + const externalData = await externalRes.json(); + + return NextResponse.json(externalData); + } catch (error) { + console.error('Error:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + + } +} + diff --git a/app/api/crates/[name]/[version]/route.ts b/app/api/crates/[name]/[version]/route.ts deleted file mode 100644 index fd7994a..0000000 --- a/app/api/crates/[name]/[version]/route.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import pool from '../../../../lib/db'; -import { Vulnerability } from '@/components/VulnerabilitiesList'; - -interface CrateVersionInfo { - name: string; - version: string; - documentation: string; - dependencies: Dependency[]; - vulnerabilities: Vulnerability[]; -} - -interface Dependency { - name: string; - version: string; -} - -type Params = Promise<{ name: string, version: string }> - -function generateRandomVulnerabilities(): Vulnerability[] { - const severities: Vulnerability['severity'][] = ['low', 'medium', 'high']; - const randomVulnerabilities: Vulnerability[] = []; - - const numberOfVulnerabilities = Math.floor(Math.random() * 5); // 0 to 4 vulnerabilities - - for (let i = 0; i < numberOfVulnerabilities; i++) { - const severity = severities[Math.floor(Math.random() * severities.length)]; - randomVulnerabilities.push({ - id: `vul-${i}`, - title: `Random Vulnerability ${i + 1}`, - description: `This is a randomly generated vulnerability of ${severity} severity.`, - severity, - }); - } - - return randomVulnerabilities; -} - -export async function GET(req: NextRequest, props: { params: Params }) { - const params = await props.params - const { name, version } = params; - const nameAndVersion = `${name}/${version}`; - - try { - const client = await pool.connect(); - - const versionRes = await client.query( - 'SELECT * FROM program_versions WHERE name_and_version = \$1', - [nameAndVersion] - ); - - const dependenciesRes = await client.query( - 'SELECT dependency_name, dependency_version FROM program_dependencies WHERE name_and_version = \$1', - [nameAndVersion] - ); - - client.release(); - - if (versionRes.rows.length === 0) { - return NextResponse.json({ error: 'Crate not found' }, { status: 404 }); - } - - const versionInfo = versionRes.rows[0]; - const dependencies = dependenciesRes.rows.map((row) => ({ - name: row.dependency_name, - version: row.dependency_version, - })); - - const vulnerabilities = generateRandomVulnerabilities(); - - const crateVersionInfo: CrateVersionInfo = { - name: versionInfo.name, - version: versionInfo.version, - documentation: versionInfo.documentation, - dependencies: dependencies, - vulnerabilities: vulnerabilities - }; - - return NextResponse.json(crateVersionInfo); - } catch (error) { - console.error('Database query error:', error); - return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); - } -} diff --git a/app/api/crates/[name]/route.ts b/app/api/crates/[name]/route.ts deleted file mode 100644 index fc4cfe8..0000000 --- a/app/api/crates/[name]/route.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import pool from '../../../lib/db'; -import { CrateInfo } from '@/app/lib/crate_info'; - -type Params = Promise<{ name: string }> - -export async function GET(req: NextRequest, props: { params: Params }) { - const params = await props.params - - const { name } = params; - - try { - const client = await pool.connect(); - - // Fetch program info by name - const programRes = await client.query( - 'SELECT * FROM programs WHERE name = \$1', - [name] - ); - - if (programRes.rows.length === 0) { - client.release(); - return NextResponse.json({ error: 'Program not found' }, { status: 404 }); - } - - const programInfo = programRes.rows[0]; - - // Fetch versions from program_versions table - const versionsRes = await client.query( - 'SELECT version FROM program_versions WHERE name = \$1', - [name] - ); - - const versions = versionsRes.rows.map((row) => row.version); - - client.release(); - - const crateInfo: CrateInfo = { - id: programInfo.id, - name: programInfo.name, - //description: programInfo.description, - description: "The ark-curve-constraint-tests crate is a comprehensive testing suite designed for validating the constraint systems of elliptic curves in the Arkworks ecosystem. It ensures that cryptographic curves are implemented correctly and efficiently within constraint systems, which are crucial for zero-knowledge proofs and other cryptographic protocols.", - repository: programInfo.github_url, - documentation: programInfo.doc_url, - downloads: programInfo.downloads, - cratesio: programInfo.cratesio, - publishedDate: '2024.8.24' - } - - return NextResponse.json({ - crateInfo: crateInfo, - versions: versions, // 添加版本信息 - vulnerabilities: [], // 如果有漏洞信息,可以在这里添加查询逻辑 - }); - } catch (error) { - console.error('Database query error:', error); - return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); - } -} - diff --git a/app/api/crates/route.ts b/app/api/crates/route.tsx similarity index 65% rename from app/api/crates/route.ts rename to app/api/crates/route.tsx index 9511e04..27c2171 100644 --- a/app/api/crates/route.ts +++ b/app/api/crates/route.tsx @@ -1,15 +1,14 @@ -import { NextResponse } from 'next/server'; +import { NextRequest, NextResponse } from 'next/server'; -export async function GET() { +export async function GET(req: NextRequest) { try { // 发送 HTTP 请求获取外部数据 - const externalApiUrl = 'http://210.28.134.203:6888/crates'; // 替换为你的外部 API URL + console.log(req); + const externalApiUrl = 'http://210.28.134.203:6888/api/crates'; // 替换为你的外部 API URL const externalRes = await fetch(externalApiUrl); - if (!externalRes.ok) { throw new Error('Failed to fetch external data'); } - const externalData = await externalRes.json(); return NextResponse.json(externalData); @@ -18,3 +17,4 @@ export async function GET() { return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } } + diff --git a/app/api/cvelist/route.tsx b/app/api/cvelist/route.tsx new file mode 100644 index 0000000..0664e53 --- /dev/null +++ b/app/api/cvelist/route.tsx @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(req: NextRequest) { + try { + // 发送 HTTP 请求获取外部数据 + console.log(req); + const externalApiUrl = 'http://210.28.134.203:6888/api/cvelist'; // 替换为你的外部 API URL + const externalRes = await fetch(externalApiUrl); + if (!externalRes.ok) { + throw new Error('Failed to fetch external data'); + } + const externalData = await externalRes.json(); + + return NextResponse.json(externalData); + } catch (error) { + console.error('Error:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} diff --git a/app/api/cves/route.ts b/app/api/cves/route.ts index 6db958a..9a4abce 100644 --- a/app/api/cves/route.ts +++ b/app/api/cves/route.ts @@ -34,7 +34,8 @@ export async function GET() { try { const randomCVEs = Array.from({ length: 7 }, generateRandomCVE); return NextResponse.json(randomCVEs); - } catch { + } catch (error) { + console.log(error); return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } } diff --git a/app/api/search/route.tsx b/app/api/search/route.tsx new file mode 100644 index 0000000..5f6e9f0 --- /dev/null +++ b/app/api/search/route.tsx @@ -0,0 +1,32 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request: Request) { + const apiUrl = `http://210.28.134.203:6888/api/search`; + const requestBody = await request.json(); + console.log("Request Body:", requestBody); + + const { query, pagination } = requestBody; //请求体 + try { + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', // 确保发送 JSON + }, + body: JSON.stringify({ + query, // 发送 query 字段 + pagination, // 发送 pagination 字段 + }), // 发送 name 字段 + }); + console.log("response:", response); + if (!response.ok) { + return NextResponse.json({ error: 'Failed to submit data' }, { status: response.status }); + } + + const result = await response.json(); // 确认返回的是 JSON 格式 + console.log("results:", result); + return NextResponse.json({ message: 'Submission successful', code: 2012, data: result }); + } catch (error) { + console.log(error); + return NextResponse.json({ error: 'An error occurred while submitting data.' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/submit/route.ts b/app/api/submit/route.ts index 259df56..f160b1c 100644 --- a/app/api/submit/route.ts +++ b/app/api/submit/route.ts @@ -1,25 +1,25 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextResponse } from 'next/server'; -export async function POST(request: NextRequest) { - const apiUrl = process.env.API_URL; // 读取环境变量,获取后端服务的基础 URL - const body = await request.json(); // 解析请求体 +export async function POST(request: Request) { + const apiUrl = process.env.API_URL; // 读取环境变量 + const formData = await request.formData(); // 解析请求体 + + console.log("Request FormData:", formData); try { - const response = await fetch(`${apiUrl}/submit`, { //向后端发送服务请求 + const response = await fetch(`${apiUrl}`, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), // 将请求体发送到后端 + body: formData, // 保持 FormData }); - //请求失败直接返回 + if (!response.ok) { - return NextResponse.json({ error: 'Failed to submit data' }, { status: 500 }); + return NextResponse.json({ error: 'Failed to submit data' }, { status: response.status }); } - //解析成功的响应 - const result = await response.json(); - return NextResponse.json({ message: 'Submission successful', data: result }); - } catch { + + const result = await response.json(); // 确认返回的是 JSON 格式 + return NextResponse.json({ message: 'Submission successful', result }); + } catch (error) { + console.log(error); return NextResponse.json({ error: 'An error occurred while submitting data.' }, { status: 500 }); } } \ No newline at end of file diff --git a/app/homepage/[name]/[version]/dependencies/page.tsx b/app/homepage/[name]/[version]/dependencies/page.tsx index e628268..a844040 100644 --- a/app/homepage/[name]/[version]/dependencies/page.tsx +++ b/app/homepage/[name]/[version]/dependencies/page.tsx @@ -1,26 +1,96 @@ -import React from 'react'; +"use client"; +import React, { useEffect, useState } from 'react'; + import Link from 'next/link'; +import DependencyTable from '../../../../../components/DependencyTable'; +import { dependenciesInfo } from '@/app/lib/all_interface'; +import { useSearchParams } from 'next/navigation'; + +// const defaultdata = { +// "direct_count": 1, +// "indirect_count": 2, +// "data": [ +// { +// "crate_name": 'unknown', +// "version": 'unknown', +// "relation": 'unknown', +// "license": 'unknown', +// "dependencies": 0, +// }, +// ] +// } + + +// interface DependencyItem { +// crate_name: string; +// version: string; +// relation: string; +// license: string; +// dependencies: number; +// } + + + const CratePage = () => { - // Other code + + const [results, setResults] = useState(null); + const [searchQuery, setSearchQuery] = useState(''); + + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const searchParams = useSearchParams(); + + const crateName = searchParams.get('crate_name'); // 从 URL 中获取 crate_name 参数 + const version = searchParams.get('version'); // 从 URL 中获取 version 参数 + + useEffect(() => { + const fetchCrateData = async () => { + try { + setError(null); + const response = await fetch(`/api/crates/${crateName}/${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(); // 调用函数来获取数据 + }, [crateName, version]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + console.log('dependencyyyyyyyyyyyyyyy', results?.data); return (
{/* Existing header and search */}
-
- open - / - source - / - insights -
+ +
+ open + / + source + / + insights +
+
- tokio + {crateName}
-
+
setSearchQuery(e.target.value)} // 更新搜索内容 /> - + + +
+ {/* 导航栏 */}
- +
); diff --git a/app/homepage/[name]/[version]/dependents/page.tsx b/app/homepage/[name]/[version]/dependents/page.tsx new file mode 100644 index 0000000..b839caa --- /dev/null +++ b/app/homepage/[name]/[version]/dependents/page.tsx @@ -0,0 +1,158 @@ +"use client"; +import React, { useEffect, useState } from 'react'; +import Link from 'next/link'; +import DependentTable from '../../../../../components/DependentTable'; +import { useSearchParams } from 'next/navigation'; +import { dependentsInfo } from '@/app/lib/all_interface'; + + + +const CratePage = () => { + const [results, setResults] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const searchParams = useSearchParams(); + const crateName = searchParams.get('crate_name'); // 从 URL 中获取 crate_name 参数 + const version = searchParams.get('version'); // 从 URL 中获取 version 参数 + + useEffect(() => { + const fetchCrateData = async () => { + try { + const response = await fetch(`/api/crates/${crateName}/${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(); // 调用函数来获取数据 + }, [crateName, version]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + + console.log('dependencyyyyyyyyyyyyyyy', results?.data); + + return ( +
+ {/* Existing header and search */} +
+
+
+ +
+ open + / + source + / + insights +
+ +
+ {crateName} +
+ + {/* 这里可以添加版本选择的下拉菜单 */} +
+
+
+
+ + +
+
+ + {/* 导航栏 */} + + +
+ + + + +
+ ); +}; + +export default CratePage; \ No newline at end of file diff --git a/app/homepage/[name]/[version]/page.tsx b/app/homepage/[name]/[version]/page.tsx index 2927325..1783b55 100644 --- a/app/homepage/[name]/[version]/page.tsx +++ b/app/homepage/[name]/[version]/page.tsx @@ -1,7 +1,64 @@ -import React from 'react'; +"use client"; +import React, { useEffect, useState } from 'react'; import Link from 'next/link'; +import { useSearchParams } from 'next/navigation'; +import { cratesInfo } from '@/app/lib/all_interface'; + const CratePage = () => { - // Other code + const [isOpen, setIsOpen] = useState(false); // 状态管理下拉菜单的显示 + + + + + const [searchQuery, setSearchQuery] = useState(''); + const [results, setResults] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const searchParams = useSearchParams(); + const crateName = searchParams.get('crate_name'); // 从 URL 中获取 crate_name 参数 + const crateVersion = searchParams.get('version'); // 从 URL 中获取 version 参数 + useEffect(() => { + const fetchCrateData = async () => { + try { + const response = await fetch(`/api/crates/${crateName}/${crateVersion}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + console.log('overviewwwwwwwwwwwwww:', data); + + setResults(data); // 设置获取的数据 + + } catch (error) { + console.log('Error fetching data:', error); + setError('An error occurred'); + } finally { + setLoading(false); // 完成加载 + } + }; + + + fetchCrateData(); // 调用函数来获取数据 + }, [crateName, crateVersion]); // 依赖项数组,确保在 crateName 或 version 改变时重新获取数据 + + + const toggleDropdown = () => { + setIsOpen(prev => !prev); + }; + + const closeDropdown = () => { + setIsOpen(false); + }; + + // 渲染部分 + if (loading) return

Loading...

; + if (error) return

Error: {error}

; + + + return (
@@ -9,18 +66,24 @@ const CratePage = () => {
-
- open - / - source - / - insights -
+ +
+ open + / + source + / + insights +
+
- tokio + {crateName} + {/*版本列表*/}
- - {/* 这里可以添加版本选择的下拉菜单 */} + {isOpen && ( +
+ {/* 遮罩层 */} +
+
+
    + {results?.versions.map((version, index) => ( + +
  • + {version} +
  • + + ))} +
+
+
+ )}
+ +
-
+
setSearchQuery(e.target.value)} // 更新搜索内容 /> - + + +
+ {/* 导航栏 */}
@@ -83,7 +204,7 @@ const CratePage = () => { {/* Security Advisories */}

Security Advisories

-

No advisories detected.

+

cve: {results ? JSON.stringify(results.cves) : 'No results available'}

{/* Licenses */}
@@ -105,27 +226,47 @@ const CratePage = () => {

Dependencies

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

Dependents

- Direct: 23 + Direct: {results ? JSON.stringify(results.dependents.direct) : 'No results available'}
- Indirect: 139 + Indirect: {results ? JSON.stringify(results.dependents.indirect) : 'No results available'}
- - View all dependencies - + +
+ View all dependencies +
+
diff --git a/app/homepage/page.tsx b/app/homepage/page.tsx index b7708b7..d09f9c6 100644 --- a/app/homepage/page.tsx +++ b/app/homepage/page.tsx @@ -1,44 +1,42 @@ -import React from 'react'; +'use client'; +import React, { useState } from 'react'; import '@/app/ui/global.css'; +import Link from 'next/link'; +import VulnerabilityList from '@/components/CveList'; const HomePage = () => { - return ( - //紫色渐变 - //
- // {/* 头部和搜索部分 */} - //
- //
- //
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.

- //
- // - // - //
- //
- //
+ const [searchQuery, setSearchQuery] = useState(''); + + const handleKeyPress = (e: { key: string; }) => { + // 检查是否按下了回车键 + if (e.key === 'Enter') { + // 如果是回车键,执行搜索 + performSearch(); + } + }; + + const performSearch = () => { + // 这里可以添加任何其他逻辑,比如验证搜索内容 + if (searchQuery.trim()) { + // 使用 Link 跳转到搜索页面 + window.location.href = `/homepage/search?crate_name=${searchQuery}`; + } + }; + + return ( //绿色渐变 - < div className="min-h-screen bg-gray-900 text-white" > -
-
open/source/insights
+ < div className=" min-h-screen bg-gray-900 text-white" > +
+ +
open/source/insights
+ +
{/* 分割线部分 */} -
+
+ + + {/* 一些介绍 */} -
+

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.

diff --git a/app/homepage/search/page.tsx b/app/homepage/search/page.tsx index 267b632..b9c332d 100644 --- a/app/homepage/search/page.tsx +++ b/app/homepage/search/page.tsx @@ -1,74 +1,89 @@ "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'; -import { useState } from 'react'; +const Search = () => { + const [results, setResults] = useState(null); + const searchParams = useSearchParams(); + const name = searchParams.get('crate_name'); -export default function Home() { - //const [query,] = useState(''); //const [query, setQuery] = useState(''); - // 使用假数据进行测试,const [results, setResults] = useState([ - const [results,] = useState([ - { crate_name: "tokio", version: "1.41.1", date: "2023-01-01" }, - { crate_name: "tokio", version: "0.1.2", date: "2023-02-01" }, - ]); + useEffect(() => { + if (name) { + fetchResults(name); // 使用 name 发起请求 + } + }, [name]); // 当 name 改变时重新运行 - // const search = async () => { - // // 待替换api - // const apiUrl = 'api'; - - // try { - // const response = await fetch(apiUrl, { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ query }), - // }); - - // const data = await response.json(); - // setResults(data.results); - // } catch (error) { - // console.error('Error fetching data:', error); - // } - // }; + const fetchResults = async (query: string) => { + try { + const response = await fetch('/api/search', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, // 将 query 作为 JSON 发送 + pagination: { + page: 1, // 页码 + 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); + } + }; return ( - //页面顶部和搜索框
-
-
-
- open - / - source - / - insights -
-
- - -
-
-
- - {/*搜索数据展示 */} +
- {results.map((item, index) => ( -
- {item.crate_name} -
Crate {item.version}Published {item.date}
-
- ))} + {results ? ( + results.data.total_page > 0 ? ( + results.data.items.map((item, index) => ( + +
+ {item.name} +
+ Crate {item.version} +
+
+ + )) + ) : ( +

No items found.

+ ) + ) : ( +

Loading...

+ )}
); +} + +export default function SearchPage() { + return ( + Loading...
}> + + + ); } \ No newline at end of file diff --git a/app/lib/all_interface.ts b/app/lib/all_interface.ts new file mode 100644 index 0000000..f8f0fec --- /dev/null +++ b/app/lib/all_interface.ts @@ -0,0 +1,93 @@ +//一、搜索结果 +export interface searchResult { + "code": number, + "message": string, + "data": { + "total_page": number, + "items": [ + { + "name": string, + "version": string, + "date": number + }, + ] + } +} + +//二、crates信息 +export interface cratesInfo { + "crate_name": string, + "description": string, + + + "dependencies": { + "direct": number, + "indirect": number + }, + "dependents": { + "direct": number, + "indirect": number + }, + + "cves": [ + { + "cve_id": string, + "url": string, + "description": string + } + ], + "versions": string[], + +} + +//三、dependencies接口 +export interface dependenciesInfo { + "direct_count": number, + "indirect_count": number, + "data": [ + { + "crate_name": string, + "version": string, + "relation": string, + "license": string, + "dependencies": number + }, + ] +} + +//四、Dependents接口 +export interface dependentsInfo { + + "direct_count": number, + "indirect_count": number, + "data": [ + { + "crate_name": string, + "version": string, + "relation": string, + }, + ] + +} + +//五、CVE List接口 +export interface cveListInfo { + "cves": [ + { + "cve_id": string, + "url": string, + "description": string, + "crate_name": string, + "start_version": string, + "end_version": string, + }, + { + "cve_id": string, + "url": string, + "description": string, + "crate_name": string, + "start_version": string, + "end_version": string, + }, + ] +} \ No newline at end of file diff --git a/components/CveList.tsx b/components/CveList.tsx new file mode 100644 index 0000000..12b3103 --- /dev/null +++ b/components/CveList.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useState } from 'react'; +import { cveListInfo } from '@/app/lib/all_interface'; +const VulnerabilityList = () => { + + const [vulnerabilities, setVulnerabilities] = useState(null); + + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + setError(null); + const response = await fetch('api/cvelist'); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const data = await response.json(); + setVulnerabilities(data); + } catch (error) { + console.log('error:', error) + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + if (loading) return
Loading...
; + if (error) return
Error: {error}
; + + // 确保 vulnerabilities 不是 null + if (!vulnerabilities) return
No vulnerabilities found.
; + + + return ( + + + + + + + + + + + + {vulnerabilities.cves.map((vuln, index) => ( + + + + + + + + ))} + +
Vuln IDDescriptionCrate NameStart VersionEnd Version
+ + {vuln.cve_id} + + {vuln.description}{vuln.crate_name}{vuln.start_version}{vuln.end_version}
+ ); + + +}; + +export default VulnerabilityList; \ No newline at end of file diff --git a/components/DependencyTable.tsx b/components/DependencyTable.tsx new file mode 100644 index 0000000..c9894ae --- /dev/null +++ b/components/DependencyTable.tsx @@ -0,0 +1,108 @@ +'use client'; +import { useMemo } from 'react'; +import React, { useState } from 'react'; +import { Table } from 'antd'; +// import { dependenciesInfo } from '@/app/lib/all_interface'; + +interface DependencyItem { + crate_name: string; + version: string; + relation: string; + license: string; + dependencies: number; +} + +interface DependencyTableProps { + data: DependencyItem[] | undefined; // 允许为 undefined +} + +const DependencyTable: React.FC = ({ data }) => { + const [sortColumn, setSortColumn] = useState(null); + const [sortDirection, setSortDirection] = useState(null); + + console.log('data in compnent dcyyyyyyyyyyyy:', data); + const x = 1; + if (x <= 0) { + setSortColumn(null); + setSortDirection(null); + console.log(data); + } + + + + const sortedData = useMemo(() => { + if (data === null) { + return []; + } + if (!data) { + return []; // 如果 data 为 undefined 或 null,返回空数组 + } + return sortColumn + ? data.sort((a: DependencyItem, b: DependencyItem) => { + if (a[sortColumn] < b[sortColumn]) return sortDirection === 'ascend' ? -1 : 1; + if (a[sortColumn] > b[sortColumn]) return sortDirection === 'ascend' ? 1 : -1; + return 0; + }) + : data; + }, [data, sortColumn, sortDirection]); + + const columns = [ + { + title: 'Crate', + dataIndex: 'crate_name', + key: 'Crate', + sorter: true, + sortDirection: sortDirection, + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'Version', + dataIndex: 'version', + key: 'Version', + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'Relation', + dataIndex: 'relation', + key: 'Relation', + sorter: true, + sortDirection: sortDirection, + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'License', + dataIndex: 'license', + key: 'License', + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'Dependencies', + dataIndex: 'dependencies', + key: 'Dependencies', + sorter: true, + sortDirection: sortDirection, + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + ]; + + // const handleSort = (column: SorterResult | SorterResult[], direction: React.SetStateAction) => { + // setSortColumn(column.dataIndex); + // setSortDirection(direction); + // }; + // const x = 1; + // if (x <= 0) { + // setSortColumn(null); + // setSortDirection(null); + // } + return ( + handleSort(sorter, sorter.order)} + // rowKey={(record) => record.Crate} + /> + ); +}; + +export default DependencyTable; \ No newline at end of file diff --git a/components/DependentTable.tsx b/components/DependentTable.tsx new file mode 100644 index 0000000..046567c --- /dev/null +++ b/components/DependentTable.tsx @@ -0,0 +1,94 @@ +'use client'; +import React, { useState, useMemo } from 'react'; +import { Table } from 'antd'; +// import { DownOutlined, UpOutlined } from '@ant-design/icons'; +// import { SorterResult } from 'antd/es/table/interface'; + +interface DependentItem { + crate_name: string; + version: string; + relation: string; +} + +interface DependentTableProps { + data: DependentItem[] | undefined; // 允许为 undefined +} + +const DependencyTable: React.FC = ({ data }) => { + const [sortColumn, setSortColumn] = useState(null); + const [sortDirection, setSortDirection] = useState(null); + + + console.log('data in compnent detttttttttttttttttttttts:', data); + const y = 1; + if (y <= 0) { + setSortColumn(null); + setSortDirection(null); + console.log(data); + } + + + const sortedData = useMemo(() => { + if (data === null) { + return []; + } + if (!data) { + return []; // 如果 data 为 undefined 或 null,返回空数组 + } + return sortColumn + ? data.sort((a: DependentItem, b: DependentItem) => { + if (a[sortColumn] < b[sortColumn]) return sortDirection === 'ascend' ? -1 : 1; + if (a[sortColumn] > b[sortColumn]) return sortDirection === 'ascend' ? 1 : -1; + return 0; + }) + : data; + }, [data, sortColumn, sortDirection]); + + const columns = [ + { + title: 'Crate', + dataIndex: 'crate_name', + key: 'Crate', + sorter: true, + sortDirection: sortDirection, + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'Version', + dataIndex: 'version', + key: 'Version', + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + { + title: 'Relation', + dataIndex: 'relation', + key: 'relation', + sorter: true, + sortDirection: sortDirection, + render: (text: string | number | bigint | boolean | React.ReactElement> | Iterable | React.ReactPortal | Promise | null | undefined) => {text}, + }, + + + ]; + + // const handleSort = (column: SorterResult | SorterResult[], direction: React.SetStateAction) => { + // setSortColumn(column.dataIndex); + // setSortDirection(direction); + // }; + const x = 1; + if (x <= 0) { + setSortColumn(null); + setSortDirection(null); + } + return ( +
handleSort(sorter, sorter.order)} + //rowKey={(record) => record.Crate} + /> + ); +}; + +export default DependencyTable; \ No newline at end of file diff --git a/components/NewHeader.tsx b/components/NewHeader.tsx new file mode 100644 index 0000000..d22f7ed --- /dev/null +++ b/components/NewHeader.tsx @@ -0,0 +1,41 @@ +import React, { useState } from "react"; +import Link from "next/link"; + +const NewHeader = () => { + const [searchQuery, setSearchQuery] = useState(''); + return ( + +
+
+ +
+ open + / + source + / + insights +
+ +
+ setSearchQuery(e.target.value)} // 更新搜索内容 + /> + + + +
+
+
+ + ); +} +export default NewHeader; \ No newline at end of file