diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx index 3234d3c..59768bf 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/layout.tsx @@ -14,11 +14,11 @@ export default function Layout({ // 判断当前页面是否为SenseLeak页面 const isSenseLeakPage = pathname.includes('/senseleak'); - + const isMircheckerPage = pathname.includes('/mirchecker'); return (
{/* 仅在非SenseLeak页面显示导航栏 */} - {!isSenseLeakPage && ( + {!isSenseLeakPage && !isMircheckerPage && ( { + const params = useParams(); + const [versionsList, setVersionsList] = useState([]); + const [mircheckerData, setMircheckerData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); + + // 添加滚动条状态和引用 + const [isScrolling, setIsScrolling] = useState(false); + const scrollTimeoutRef = useRef(); + const containerRef = useRef(null); + + // 添加滚动位置状态 + const [scrollPosition, setScrollPosition] = useState(0); + + // 获取版本列表和Mirchecker数据 + useEffect(() => { + const fetchData = async () => { + try { + // 获取版本列表 + const versionsResponse = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}`); + if (!versionsResponse.ok) { + throw new Error(`HTTP error! status: ${versionsResponse.status}`); + } + const versionsData = await versionsResponse.json(); + setVersionsList(versionsData.versions || []); + + // 获取 Mirchecker 数据 + const mircheckerResponse = await fetch(`/api/crates/${params.nsfront}/${params.nsbehind}/${params.name}/${params.version}/mirchecker`); + if (!mircheckerResponse.ok) { + throw new Error(`HTTP error! status: ${mircheckerResponse.status}`); + } + const mircheckerData = await mircheckerResponse.json(); + setMircheckerData(mircheckerData); + + setLoading(false); + } catch (error) { + console.error('Error fetching data:', error); + setError('获取数据时出错'); + setLoading(false); + } + }; + + fetchData(); + }, [params.nsfront, params.nsbehind, params.name, params.version]); + + // 当前选中的版本 + const currentVersion = params.version as string; + + // 添加过滤后的版本列表计算 + const filteredVersions = versionsList.filter(version => + version.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + // 处理滚动事件 + const handleScroll = (e: React.UIEvent) => { + setScrollPosition(e.currentTarget.scrollTop); + setIsScrolling(true); + + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current); + } + + scrollTimeoutRef.current = setTimeout(() => { + setIsScrolling(false); + }, 1500); + }; + + // 处理搜索输入 + const handleSearch = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value); + if (containerRef.current) { + containerRef.current.scrollTop = 0; + setScrollPosition(0); + } + }; + + // 清理定时器 + useEffect(() => { + return () => { + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current); + } + }; + }, []); + + if (loading) return ( +
+

加载中...

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

错误: {error}

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

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

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

+ no result +

+
+ ) + ) : ( +
+ Run failed +

+ run failed +

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

+ 加载中... +

+
+ )} +
+
+
+
+ ); +}; + +export default MircheckerPage; diff --git a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx index c23a130..dfc63c4 100644 --- a/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx +++ b/app/[nsfront]/[nsbehind]/[name]/[version]/page.tsx @@ -580,7 +580,6 @@ const CratePage = () => {
- {/*

Documentation URL

*/} {
+ {/* Mirchecker 部分 - 新增 */} +
+
+ icon +

+ Mirchecker +

+
+
+
+ + {basePath + '/mirchecker' || 'No results available'} + +
+
+
{/* OpenSSF Scorecard */}
diff --git a/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/mirchecker/route.tsx b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/mirchecker/route.tsx new file mode 100644 index 0000000..26c2224 --- /dev/null +++ b/app/api/crates/[nsfront]/[nsbehind]/[cratename]/[version]/mirchecker/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}/mirchecker`; + 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