mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-23 11:41:53 +08:00
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n
## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池
## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)
## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建
## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
JSON 输出格式、Redis 检测、Context 超时等
## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
145 lines
5.1 KiB
TypeScript
145 lines
5.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Terminal, Languages, Moon, Sun, Radar, BarChart3, Github, ExternalLink } from 'lucide-react';
|
|
import { ScanPage } from '@/pages/ScanPage';
|
|
import { ResultsPage } from '@/pages/ResultsPage';
|
|
import { LiveFeedProvider } from '@/contexts/LiveFeedContext';
|
|
import { Button } from '@/components/ui/button';
|
|
import './i18n';
|
|
|
|
type Page = 'scan' | 'results';
|
|
|
|
function App() {
|
|
const { t, i18n } = useTranslation();
|
|
const [currentPage, setCurrentPage] = useState<Page>('scan');
|
|
const [isDark, setIsDark] = useState(() => {
|
|
if (typeof window !== 'undefined') {
|
|
const stored = localStorage.getItem('theme');
|
|
if (stored) return stored === 'dark';
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
useEffect(() => {
|
|
document.documentElement.classList.toggle('dark', isDark);
|
|
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
}, [isDark]);
|
|
|
|
return (
|
|
<LiveFeedProvider>
|
|
<div className="min-h-screen bg-background flex flex-col">
|
|
{/* Header */}
|
|
<header className="sticky top-0 z-50 border-b bg-background/95 backdrop-blur-sm">
|
|
<div className="container h-14 sm:h-16 flex items-center justify-between">
|
|
{/* Left: Logo + Nav */}
|
|
<div className="flex items-center gap-4 sm:gap-6">
|
|
{/* Brand */}
|
|
<a
|
|
href="https://github.com/shadow1ng/fscan"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 text-foreground hover:opacity-80 transition-opacity"
|
|
>
|
|
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-lg bg-foreground flex items-center justify-center">
|
|
<Terminal className="w-4 h-4 sm:w-5 sm:h-5 text-background" />
|
|
</div>
|
|
<span className="font-semibold text-base sm:text-lg tracking-tight">fscan</span>
|
|
<span className="text-xs text-muted-foreground font-mono px-1.5 py-0.5 rounded bg-muted hidden sm:inline">v2.1</span>
|
|
</a>
|
|
|
|
<div className="h-5 w-px bg-border hidden sm:block" />
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex items-center gap-1">
|
|
<Button
|
|
variant={currentPage === 'scan' ? 'default' : 'ghost'}
|
|
size="sm"
|
|
onClick={() => setCurrentPage('scan')}
|
|
className="gap-2"
|
|
>
|
|
<Radar className="w-4 h-4" />
|
|
<span className="hidden sm:inline">{t('navScan')}</span>
|
|
</Button>
|
|
<Button
|
|
variant={currentPage === 'results' ? 'default' : 'ghost'}
|
|
size="sm"
|
|
onClick={() => setCurrentPage('results')}
|
|
className="gap-2"
|
|
>
|
|
<BarChart3 className="w-4 h-4" />
|
|
<span className="hidden sm:inline">{t('navResults')}</span>
|
|
</Button>
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Right: Actions */}
|
|
<div className="flex items-center gap-1">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
asChild
|
|
>
|
|
<a
|
|
href="https://github.com/shadow1ng/fscan"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title="GitHub"
|
|
>
|
|
<Github className="w-5 h-5" />
|
|
</a>
|
|
</Button>
|
|
<div className="h-5 w-px bg-border mx-1" />
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => i18n.changeLanguage(i18n.language === 'zh' ? 'en' : 'zh')}
|
|
title={i18n.language === 'zh' ? 'English' : '中文'}
|
|
>
|
|
<Languages className="w-5 h-5" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => setIsDark(!isDark)}
|
|
title={isDark ? t('lightMode') : t('darkMode')}
|
|
>
|
|
{isDark ? (
|
|
<Sun className="w-5 h-5" />
|
|
) : (
|
|
<Moon className="w-5 h-5" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Main */}
|
|
<main className="container flex-1 py-3 sm:py-4 lg:py-5">
|
|
{currentPage === 'scan' ? <ScanPage /> : <ResultsPage />}
|
|
</main>
|
|
|
|
{/* Footer */}
|
|
<footer className="border-t py-3 sm:py-4">
|
|
<div className="container flex items-center justify-center gap-2 text-xs sm:text-sm text-muted-foreground">
|
|
<Terminal className="w-4 h-4" />
|
|
<span>{t('appDescription')}</span>
|
|
<span className="opacity-40">·</span>
|
|
<a
|
|
href="https://github.com/shadow1ng/fscan"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-1 hover:text-foreground transition-colors"
|
|
>
|
|
GitHub
|
|
<ExternalLink className="w-3.5 h-3.5" />
|
|
</a>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</LiveFeedProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|