From 3556614d93c51317fe1906b505d8e7692051fb3c Mon Sep 17 00:00:00 2001 From: mapan-nju <2246839805@qq.com> Date: Tue, 3 Dec 2024 16:28:17 +0800 Subject: [PATCH 1/2] aaa --- app/context/CrateContext.tsx | 64 +++++++ .../[name]/[version]/dependencies/page.tsx | 101 +---------- .../[name]/[version]/dependents/page.tsx | 94 +--------- .../[nsbehind]/[name]/[version]/page.tsx | 144 ++-------------- app/homepage/[nsfront]/[nsbehind]/layout.tsx | 24 +++ app/homepage/layout.tsx | 10 +- components/HeaderWithSearch.tsx | 160 ++++++++++++++++++ next.config.mjs | 9 + 8 files changed, 287 insertions(+), 319 deletions(-) create mode 100644 app/context/CrateContext.tsx create mode 100644 app/homepage/[nsfront]/[nsbehind]/layout.tsx create mode 100644 components/HeaderWithSearch.tsx diff --git a/app/context/CrateContext.tsx b/app/context/CrateContext.tsx new file mode 100644 index 0000000..52becc4 --- /dev/null +++ b/app/context/CrateContext.tsx @@ -0,0 +1,64 @@ +// context/HeaderContext.tsx +"use client"; +import React, { createContext, useContext, useState } from 'react'; + +// 定义 cratesInfo 接口 +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[]; +} + +// 定义 HeaderContext 的类型 +interface CrateData { + crateName: string | undefined; + crateVersion: string | string[] | undefined; + results: CratesInfo | null; // 存储 cratesInfo 数据 +} + +interface HeaderContextType { + crateData: CrateData; + setCrateData: React.Dispatch>; +} + +// 创建上下文,并设置默认值为 undefined +const HeaderContext = createContext(undefined); + +// 创建提供者组件 +export const HeaderProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [crateData, setCrateData] = useState({ + crateName: undefined, + crateVersion: undefined, + results: null, + }); + + // 这里可以添加一些副作用,例如在 crateData 更新时执行某些操作 + + return ( + + {children} + + ); +}; + +// 创建一个自定义 Hook 用于使用上下文 +export const useHeaderContext = (): HeaderContextType => { + const context = useContext(HeaderContext); + if (context === undefined) { + throw new Error('useHeaderContext must be used within a HeaderProvider'); + } + return context; +}; \ 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 index cdbfc35..fc966f1 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependencies/page.tsx @@ -1,21 +1,22 @@ +//Dependencies页面 "use client"; import React, { useEffect, useState } from 'react'; import { useParams } from 'next/navigation' -import Link from 'next/link'; + import DependencyTable from '../../../../../../../components/DependencyTable'; import { dependenciesInfo } from '@/app/lib/all_interface'; + const CratePage = () => { const params = useParams(); const [results, setResults] = useState(null); - const [searchQuery, setSearchQuery] = useState(''); + const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const version = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { @@ -45,100 +46,12 @@ const CratePage = () => { if (error) return
Error: {error}
; // console.log('dependencyyyyyyyyyyyyyyy', results?.data); return ( -
+ <> {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} -
- - {/* 这里可以添加版本选择的下拉菜单 */} -
-
-
-
- setSearchQuery(e.target.value)} // 更新搜索内容 - /> - - - -
-
- - {/* 导航栏 */} - -
+ -
); }; diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx index ab776fb..84d0386 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/dependents/page.tsx @@ -1,11 +1,13 @@ +//Dependents页面 "use client"; import React, { useEffect, useState } from 'react'; -import Link from 'next/link'; + 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); @@ -13,8 +15,7 @@ const CratePage = () => { const params = useParams(); - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const version = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { @@ -43,95 +44,10 @@ const CratePage = () => { if (loading) return
Loading...
; if (error) return
Error: {error}
; - //console.log('dependencyyyyyyyyyyyyyyy', results?.data); + return (
- {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} -
- - {/* 这里可以添加版本选择的下拉菜单 */} -
-
-
-
- - -
-
- - {/* 导航栏 */} - - -
- diff --git a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx index e3d2c56..1695a6e 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -1,28 +1,30 @@ +//Overview页面 "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'; -import { useParams } from 'next/navigation' +import { useParams } from 'next/navigation'; + + + const CratePage = () => { - const [isOpen, setIsOpen] = useState(false); // 状态管理下拉菜单的显示 + const params = useParams(); - //console.log("params:", params); - 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 参数 - const crateName = params.name; // 从 URL 中获取 crate_name 参数 - const crateVersion = params.version; // 从 URL 中获取 version 参数 + useEffect(() => { const fetchCrateData = async () => { try { @@ -49,15 +51,6 @@ const CratePage = () => { fetchCrateData(); // 调用函数来获取数据 }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 name 或 version 改变时重新获取数据 - - const toggleDropdown = () => { - setIsOpen(prev => !prev); - }; - - const closeDropdown = () => { - setIsOpen(false); - }; - // 渲染部分 if (loading) return

Loading...

; if (error) return

Error: {error}

; @@ -67,121 +60,6 @@ const CratePage = () => { return (
- {/* Existing header and search */} -
-
-
- -
- open - / - source - / - insights -
- -
- {crateName} - {/*版本列表*/} -
- - {isOpen && ( -
- {/* 遮罩层 */} -
-
-
    - {results?.versions.map((version, index) => ( - -
  • - {version} -
  • - - ))} -
-
-
- )} -
- - -
-
-
- setSearchQuery(e.target.value)} // 更新搜索内容 - /> - - - -
-
- - {/* 导航栏 */} - -
- {/* cve */}
diff --git a/app/homepage/[nsfront]/[nsbehind]/layout.tsx b/app/homepage/[nsfront]/[nsbehind]/layout.tsx new file mode 100644 index 0000000..4b5052a --- /dev/null +++ b/app/homepage/[nsfront]/[nsbehind]/layout.tsx @@ -0,0 +1,24 @@ +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 index 30e72fa..d80a618 100644 --- a/app/homepage/layout.tsx +++ b/app/homepage/layout.tsx @@ -1,5 +1,5 @@ import '@/app/ui/global.css'; - +import { HeaderProvider } from '../context/CrateContext'; export const metadata = { title: 'cratespro', @@ -12,8 +12,12 @@ export default function RootLayout({ children: React.ReactNode }) { return ( - - {children} + + + + {children} + + ) } diff --git a/components/HeaderWithSearch.tsx b/components/HeaderWithSearch.tsx new file mode 100644 index 0000000..bcbe1d7 --- /dev/null +++ b/components/HeaderWithSearch.tsx @@ -0,0 +1,160 @@ +// components/Header.js +'use client'; +import React, { useEffect, useState } from 'react'; +import Link from 'next/link'; +import { useHeaderContext } from '../app/context/CrateContext'; +import { useParams } from 'next/navigation'; +// import { cratesInfo } from '@/app/lib/all_interface'; + +const Header = () => { + const params = useParams(); + + const [searchQuery, setSearchQuery] = useState(''); + const { crateData, setCrateData } = useHeaderContext(); + const [isOpen, setIsOpen] = useState(false); + //const [currentVersion, setCurrentVersion] = useState(); // 存储当前选中的版本 + + const [currentVersion, setCurrentVersion] = useState(params.version); // 存储当前选中的版本 + + const [activeTab, setActiveTab] = useState('overview'); // 默认选中 Overview + // 定义导航项数据 + const navItems = [ + { name: 'Overview', path: '' }, + { name: 'Dependencies', path: '/dependencies' }, + { name: 'Dependents', path: '/dependents' }, + ]; + + + // 使用 useEffect 从 API 获取数据 + useEffect(() => { + const fetchData = async () => { + // 如果 crateData.results 为空,说明数据还未加载 + if (!crateData.results) { + const response = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + const data = await response.json(); + console.log('dataaaaaaaaaaaaaa:', data); + setCrateData({ + crateName: data.crate_name, + crateVersion: params.version, + results: data, + }); + + } + }; + + fetchData(); + }, [params.nsfront, params.nsbehind, params.name, params.version, setCrateData, crateData.crateVersion, crateData.results]); // 添加 crateData 作为依赖项 + + const toggleDropdown = () => { + setIsOpen((prev) => !prev); + }; + + const closeDropdown = () => { + setIsOpen(false); + + }; + + + + + + return ( +
+
+
+ +
+ open + / + source + / + insights +
+ +
+ {params.name} +
+ + {isOpen && ( +
+
+
+
    + {crateData.results?.versions.map((version, index) => ( + setCurrentVersion(version)} + href={`/homepage/${params.nsfront}/${params.nsbehind}/${crateData.results?.crate_name}/${version}`} + > +
  • + + {version} + +
  • + + ))} +
+
+
+ )} +
+
+
+
+ setSearchQuery(e.target.value)} + /> + + + +
+
+ +
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 8ed419e..2264c4b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,6 +3,7 @@ // next.config.mjs export default { + async redirects() { return [ { @@ -13,3 +14,11 @@ export default { ]; }, }; +// /** @type {import('next').NextConfig} */ +// const nextConfig = { +// experimental: { +// ppr: 'incremental', +// }, +// } + +// module.exports = nextConfig \ No newline at end of file From 71a4e37191f1f7c8a29e7f73464c49ef5d8194c0 Mon Sep 17 00:00:00 2001 From: mapan-nju <2246839805@qq.com> Date: Tue, 10 Dec 2024 18:54:57 +0800 Subject: [PATCH 2/2] solved some bugs --- Dockerfile | 29 ++++ .../[nsbehind]/[name]/[version]/page.tsx | 152 +++++++----------- app/homepage/page.tsx | 2 +- app/homepage/search/page.tsx | 57 ++++--- app/homepage/styles.css | 5 + app/lib/all_interface.ts | 11 +- app/newpage/layout.tsx | 2 +- app/programs/layout.tsx | 2 +- app/ui/global.css | 5 + next.config.mjs | 17 +- 10 files changed, 144 insertions(+), 138 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b0a845f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM node:22.12.0-alpine3.19 AS builder +WORKDIR /app + +COPY package*.json ./ + +RUN npm ci + +COPY . . + +RUN npm run build + + + + + +# 使用轻量级的 Node.js 镜像 +FROM node:22.12.0-alpine3.19 AS runner + +# 设置工作目录 +WORKDIR /app + +# 复制构建好的文件 +COPY --from=builder /app ./ + +# 暴露应用运行的端口 +EXPOSE 3000 + +# 启动应用 +CMD ["npm", "start"] \ 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 index 1695a6e..ce980c4 100644 --- a/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/homepage/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -1,78 +1,55 @@ -//Overview页面 +// Overview页面 "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'; 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(); - console.log('overviewwwwwwwwwwwwww:', data); - - setResults(data); // 设置获取的数据 - + setResults(data); } catch (error) { - console.log('Error fetching data:', error); + console.error('Error fetching data:', error); setError('An error occurred'); } finally { - setLoading(false); // 完成加载 + setLoading(false); } }; + fetchCrateData(); + }, [params.name, params.version, params.nsfront, params.nsbehind]); - fetchCrateData(); // 调用函数来获取数据 - }, [params.name, params.version, params.nsfront, params.nsbehind]); // 依赖项数组,确保在 name 或 version 改变时重新获取数据 - - // 渲染部分 if (loading) return

Loading...

; if (error) return

Error: {error}

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

Security Advisories

-

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

+

+ cve: {results && results.cves && results.cves.length > 0 && results.cves[0] !== '' ? JSON.stringify(results.cves) : 'No results available'} +

{/* Licenses */}

Licenses

- LICENSES: MIT + LICENSES: {results ? results.license : 'No results available'}
DEPENDENCY LICENSES: @@ -80,7 +57,6 @@ const CratePage = () => {
  • Apache-2.0 OR MIT (116)
  • MIT (27)
  • MIT OR Uniclicense (7)
  • - {/* Add more dependency licenses */}
    @@ -88,17 +64,13 @@ const CratePage = () => {

    Dependencies

    - Direct: {results ? JSON.stringify(results.dependencies.direct) : 'No results available'} + Direct: {results ? JSON.stringify(results.dependencies.direct) : 'No cves detected.'}
    Indirect: {results ? JSON.stringify(results.dependencies.indirect) : 'No results available'}
    - -
    - View all dependencies -
    + +
    View all dependencies
    {/* Dependents */} @@ -110,66 +82,64 @@ const CratePage = () => {
    Indirect: {results ? JSON.stringify(results.dependents.indirect) : 'No results available'}
    - -
    - View all dependencies -
    + +
    View all dependents
    -
    -

    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-Review - 10/10 -
    -
    - Maintained - 10/10 -
    -
    - CI/Best-Practices - 0/10 -
    -
    - License - 10/10 -
    -
    - Dangerous-Workflow - 10/10 -
    -
    - Security-Policy - 10/10 + + {/* 新增的块: doc_url 和 github_url */} +
    +
    +

    Documentation & GitHub Links

    + -
    - Token-Permissions - 10/10 + -
    - Binary-Artifacts - 10/10 +
    + + {/* 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.
    -
    - Pinned-Dependencies - 0/10 +
    +
    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
    -
    +
    ); }; diff --git a/app/homepage/page.tsx b/app/homepage/page.tsx index b209110..90c562a 100644 --- a/app/homepage/page.tsx +++ b/app/homepage/page.tsx @@ -84,7 +84,7 @@ const HomePage: React.FC = () => { {/* 一些介绍 */} -
    +

    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 bb3bc55..0696f43 100644 --- a/app/homepage/search/page.tsx +++ b/app/homepage/search/page.tsx @@ -8,6 +8,7 @@ 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'); @@ -18,6 +19,7 @@ const Search = () => { }, [name, currentPage]); // 当 name 或 currentPage 改变时重新运行 const fetchResults = async (query: string, page: number) => { + setLoading(true); // 开始加载数据 try { const response = await fetch('/api/search', { method: 'POST', @@ -40,6 +42,8 @@ const Search = () => { setResults(data); // 假设返回的数据data字段 } catch (error) { console.error('Error fetching data:', error); + } finally { + setLoading(false); // 数据加载完成 } }; @@ -57,12 +61,14 @@ const Search = () => { console.log("results:", results?.data.items); return ( -
    +
    -
    +
    - {results ? ( - results.data.total_page > 0 ? ( + {loading ? ( +

    Loading...

    + ) : results ? ( + results.data.total_page > 0 && results.data.items.length > 0 ? ( results.data.items.map((item, index) => ( {

    Loading...

    )}
    +
    - {/* 显示当前页数和总页数 */} + {/* 当前页数在按钮上方 */} +
    {results && ( -
    +

    - 当前页: {currentPage} / 总页数: {results.data.total_page} + Current page: {currentPage} / Total page: {results.data.total_page}

    )} - -
    - - -
    + {results && results.data.total_page > 0 && ( +
    + + +
    + )}
    ); diff --git a/app/homepage/styles.css b/app/homepage/styles.css index e69de29..7a30764 100644 --- a/app/homepage/styles.css +++ b/app/homepage/styles.css @@ -0,0 +1,5 @@ +/* styles.css */ +.custom-margin-left { + margin-left: 100px; + /* 自定义的左边距 */ +} \ No newline at end of file diff --git a/app/lib/all_interface.ts b/app/lib/all_interface.ts index d6eb8cd..b6c6891 100644 --- a/app/lib/all_interface.ts +++ b/app/lib/all_interface.ts @@ -31,13 +31,10 @@ export interface cratesInfo { "indirect": number }, - "cves": [ - { - "cve_id": string, - "url": string, - "description": string - } - ], + "cves": string[], + "license": string, + "github_url": string, + "doc_url": string, "versions": string[], } diff --git a/app/newpage/layout.tsx b/app/newpage/layout.tsx index 30e72fa..b06395b 100644 --- a/app/newpage/layout.tsx +++ b/app/newpage/layout.tsx @@ -6,7 +6,7 @@ export const metadata = { description: 'Generated by Next.js', } -export default function RootLayout({ +export default function Layout({ children, }: { children: React.ReactNode diff --git a/app/programs/layout.tsx b/app/programs/layout.tsx index 8aa80cf..a146b7b 100644 --- a/app/programs/layout.tsx +++ b/app/programs/layout.tsx @@ -2,7 +2,7 @@ import '@/app/ui/global.css'; import { inter } from '@/app/ui/fonts'; import SideNav from '@/app/ui/programs/sidenav'; -export default function RootLayout({ children }: { children: React.ReactNode }) { +export default function Layout({ children }: { children: React.ReactNode }) { return ( diff --git a/app/ui/global.css b/app/ui/global.css index c06d6d6..8a291b5 100644 --- a/app/ui/global.css +++ b/app/ui/global.css @@ -16,3 +16,8 @@ input[type='number']::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } + +.custom-margin-left { + margin-left: 500px; + /* 自定义的左边距 */ +} \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 2264c4b..32c05ad 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,24 +1,15 @@ /** @type {import('next').NextConfig} */ // next.config.mjs - export default { - async redirects() { return [ { source: '/', - destination: '/programs', - permanent: true, // 301 redirection + //destination: '/programs', + destination: '/homepage', + permanent: true, }, ]; }, -}; -// /** @type {import('next').NextConfig} */ -// const nextConfig = { -// experimental: { -// ppr: 'incremental', -// }, -// } - -// module.exports = nextConfig \ No newline at end of file +}; \ No newline at end of file