diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 119bdd8..0000000 --- a/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -# 多阶段构建 - 构建阶段 -FROM golang:1.21-alpine AS builder - -# 设置工作目录 -WORKDIR /app - -# 安装必要的工具 -RUN apk add --no-cache git ca-certificates tzdata - -# 复制go.mod和go.sum文件 -COPY go.mod go.sum ./ - -# 下载依赖 -RUN go mod download - -# 复制源代码 -COPY . . - -# 构建应用程序 -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main cmd/main.go - -# 运行阶段 -FROM alpine:latest - -# 安装ca-certificates用于HTTPS请求 -RUN apk --no-cache add ca-certificates tzdata - -# 设置时区 -ENV TZ=Asia/Shanghai - -WORKDIR /root/ - -# 从构建阶段复制二进制文件 -COPY --from=builder /app/main . - -# 复制静态文件 -COPY --from=builder /app/web ./web - -# 创建必要的目录 -RUN mkdir -p uploads logs - -# 设置权限 -RUN chmod +x ./main - -# 暴露端口 -EXPOSE 8080 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 - -# 运行应用程序 -CMD ["./main"] diff --git a/README.md b/README.md index 4a8ab03..ad6f6c3 100644 --- a/README.md +++ b/README.md @@ -1,455 +1,63 @@ -# 传传传 - 跨平台文件传输工具 +# 文件快传 - P2P文件传输工具 -> 简单、快速、安全的点对点文件传输解决方案 +![项目演示](img.png) + +> 安全、快速、简单的点对点文件传输解决方案 - 无需注册,即传即用 ## ✨ 核心功能 -- 📁 **文件传输** - 支持多文件同时传输,基于WebRTC的P2P技术 -- 📝 **文字传输** - 快速分享文本内容,支持大文本传输 -- 🖥️ **桌面共享** - 实时屏幕共享功能(开发中) -- 🔗 **URL路由** - 支持直链分享特定功能和模式 - -## 🛡️ 安全特性 - -- **端到端加密** - WebRTC内置加密,数据传输安全 -- **无文件存储** - 服务器不存储任何文件内容 -- **临时连接** - 传输完成后自动清理连接 -- **房间隔离** - 每个取件码对应独立的传输房间 +- 📁 **文件传输** - 支持多文件同时传输,基于WebRTC的P2P直连 +- 📝 **文字传输** - 快速分享文本内容 +- 🖥️ **桌面共享** - 实时屏幕共享(开发中) +- 🔒 **端到端加密** - 数据传输安全,服务器不存储文件 +- 📱 **响应式设计** - 完美适配手机、平板、电脑 ## 🚀 技术栈 -**前端架构** -- Next.js 15 + React 18 + TypeScript -- Tailwind CSS + 毛玻璃效果UI -- WebRTC DataChannel + WebSocket +**前端** - Next.js 15 + React 18 + TypeScript + Tailwind CSS +**后端** - Go + WebSocket + 内存存储 +**传输** - WebRTC DataChannel + P2P直连 -**后端架构** -- Go + Gin框架 + WebSocket -- 内存存储 + 房间管理 -- Docker容器化部署 - -## 📦 快速开始 - -### 方式一:Docker一键部署(推荐[未变写完成]) +## 📦 快速部署 ```bash git clone https://github.com/MatrixSeven/file-transfer-go.git cd file-transfer-go docker-compose up -d - -# 访问应用 -open http://localhost:8080 ``` -### 方式二:本地开发 +访问 http://localhost:8080 开始使用 + +## 🎯 使用方法 + +**发送文件** +1. 选择文件 → 生成取件码 → 分享6位码 + +**接收文件** +1. 输入取件码 → 自动连接 → 下载文件 + +## 📊 项目架构 + +``` +发送方 ←─── WebSocket信令 ───→ 服务器 ←─── WebSocket信令 ───→ 接收方 + │ │ + └────────────── WebRTC P2P直连传输 ──────────────────────────┘ +``` + +## 🛠️ 本地开发 ```bash -# 1. 启动后端服务 +# 后端 make dev -# 2. 启动前端服务 -cd chuan-next -yarn -yarn dev - -# 访问应用 -open http://localhost:3000 +# 前端 +cd chuan-next && yarn && yarn dev ``` -## 🎯 URL路由支持 - -支持通过URL参数直接跳转到特定功能: - -```bash -# 文件传输 -/?type=file&mode=send # 发送文件 -/?type=file&mode=receive # 接收文件 - -# 文字传输 -/?type=text&mode=send # 发送文字 -/?type=text&mode=receive # 接收文字 - -# 桌面共享 -/?type=desktop&mode=send # 共享桌面 -/?type=desktop&mode=receive # 观看桌面 -``` - -## 🌟 项目特色 - -- ⚡ **零配置** - 无需注册登录,即开即用 -- 🔒 **点对点** - 基于WebRTC的直接传输,服务器仅做信令 -- 📱 **响应式** - 完美适配手机、平板、电脑 -- � **现代UI** - 精美的毛玻璃效果,流畅的动画 -- 🚀 **高性能** - 64KB分块传输,支持大文件高速传输 - -## 📊 系统架构 - -``` -┌─────────────────┐ WebSocket ┌──────────────┐ WebSocket ┌─────────────────┐ -│ 发送方 (A) │ ←──────────────→ │ 信令服务器 │ ←──────────────→ │ 接收方 (B) │ -│ │ │ │ │ │ -│ - 选择文件 │ │ - 房间管理 │ │ - 输入取件码 │ -│ - 生成取件码 │ │ - 信令转发 │ │ - 获取文件列表 │ -│ - 等待连接 │ │ - 状态同步 │ │ - 下载文件 │ -└─────────────────┘ └──────────────┘ └─────────────────┘ - │ │ - │ WebRTC P2P │ - │ ┌─────────────────┐ │ - └────────────────────→│ 直接文件传输 │←──────────────────────────────┘ - │ │ - │ - 端到端加密 │ - │ - 高速传输 │ - │ - 断点续传 │ - └─────────────────┘ -``` - -## 📁 项目结构 - -``` -. -├── cmd/ # Go应用入口 -├── internal/ # Go后端核心代码 -│ ├── handlers/ # HTTP和WebSocket处理器 -│ ├── models/ # 数据模型 -│ └── services/ # 业务服务层 -├── chuan-next/ # Next.js前端应用 -│ ├── src/app/ # 应用页面 -│ ├── src/components/ # 组件库 -│ └── src/hooks/ # React Hooks -├── web/ # 静态资源(测试页面) -├── docker-compose.yml # Docker部署配置 -└── Makefile # 构建脚本 -``` - -## 🤝 贡献指南 - -欢迎提交Issue和Pull Request来帮助改进项目! - ## 📄 许可证 MIT License -│ 发送方浏览器 │◄────────┤ 信令服务器 ├────────►│ 接收方浏览器 │ -│ │ │ │ │ │ -│ ┌───────────┐ │ │ ┌──────────┐ │ │ ┌───────────┐ │ -│ │ 文件选择 │ │ │ │ WebSocket│ │ │ │ 取件码输入│ │ -│ └───────────┘ │ │ │ 信令 │ │ │ └───────────┘ │ -│ ┌───────────┐ │ │ └──────────┘ │ │ ┌───────────┐ │ -│ │ 生成取件码│ │ │ ┌──────────┐ │ │ │ 文件接收 │ │ -│ └───────────┘ │ │ │ 房间管理 │ │ │ └───────────┘ │ -│ │ │ └──────────┘ │ │ │ -└─────────────────┘ └──────────────┘ └─────────────────┘ - │ │ - └─────────────────── WebRTC P2P 连接 ──────────────────┘ - (文件直接传输) -``` - -## 🛠️ 技术栈 - -### 后端 -- **Go 1.21+**:高性能Web服务器 -- **Chi Router**:轻量级HTTP路由 -- **Gorilla WebSocket**:WebSocket连接管理 -- **标准库**:HTML模板、JSON处理等 - -### 前端 -- **原生JavaScript**:无框架依赖 -- **WebRTC API**:浏览器P2P通信 -- **Tailwind CSS**:现代化UI样式 -- **模块化设计**:分离的JS文件结构 - -### 基础设施 -- **Docker支持**:容器化部署 -- **Nginx代理**:生产环境反向代理 -- **STUN服务器**:NAT穿透支持 - -## 📁 项目结构 - -``` -chuan/ -├── cmd/ -│ └── main.go # 程序入口 -├── internal/ -│ ├── handlers/ -│ │ └── handlers.go # HTTP请求处理 -│ ├── models/ -│ │ └── models.go # 数据模型定义 -│ └── services/ -│ ├── file_service.go # 文件服务(预留) -│ ├── memory_store.go # 内存存储 -│ ├── p2p_service.go # P2P连接管理 -│ └── webrtc_service.go # WebRTC服务(预留) -├── web/ -│ ├── static/ -│ │ ├── css/ -│ │ │ └── style.css # 样式文件 -│ │ └── js/ -│ │ ├── common.js # 通用工具函数 -│ │ ├── p2p-transfer.js # P2P传输核心逻辑 -│ │ ├── webrtc-connection.js # WebRTC连接管理 -│ │ └── file-transfer.js # 文件传输处理 -│ └── templates/ -│ ├── base.html # 基础模板 -│ ├── index.html # 主页面 -│ ├── upload.html # 上传页面(预留) -│ └── video.html # 视频传输页面 -├── uploads/ # 上传目录(预留) -├── bin/ -│ └── chuan # 编译后的可执行文件 -├── docker-compose.yml # Docker Compose配置 -├── Dockerfile # Docker镜像构建 -├── nginx.conf # Nginx配置 -├── Makefile # 构建脚本 -├── deploy.sh # 部署脚本 -├── go.mod # Go模块定义 -├── go.sum # Go模块校验 -└── README.md # 项目文档 -``` - -## 🚀 快速开始 - -### 本地开发 - -1. **克隆项目** -```bash -git clone -cd chuan -``` - -2. **安装依赖** -```bash -go mod tidy -``` - -3. **启动服务** -```bash -go run cmd/main.go -``` - -4. **访问应用** -``` -打开浏览器访问: http://localhost:8080 -``` - -### 使用Make命令 - -```bash -# 运行开发服务器 -make run - -# 构建可执行文件 -make build - -# 清理构建文件 -make clean - -# 运行测试 -make test -``` - -### Docker部署 - -1. **构建镜像** -```bash -docker build -t chuan . -``` - -2. **运行容器** -```bash -docker run -p 8080:8080 chuan -``` - -3. **使用Docker Compose** -```bash -docker-compose up -d -``` - -## 📖 使用说明 - -### 发送文件 - -1. 访问主页面 -2. 点击选择文件或拖拽文件到上传区域 -3. 点击"生成取件码"按钮 -4. 分享6位取件码给接收方 -5. 等待接收方连接并开始传输 - -### 接收文件 - -1. 访问主页面 -2. 在"输入取件码"区域输入6位取件码 -3. 系统自动连接并显示文件列表 -4. 等待P2P连接建立(显示绿色状态) -5. 点击"下载"按钮开始接收文件 - -### 传输状态说明 - -- 🟡 **等待连接**:正在建立WebSocket连接 -- 🟢 **P2P已连接**:可以开始文件传输 -- 🔴 **连接失败**:请检查网络或重试 - -## ⚙️ 配置说明 - -### 环境变量 - -- `PORT`:服务器端口(默认8080) -- `HOST`:服务器主机(默认localhost) - -### STUN服务器配置 - -系统默认使用以下STUN服务器(按优先级): - -1. `stun:stun.chat.bilibili.com:3478` - 哔哩哔哩 -2. `stun:stun.voipbuster.com` - VoIP服务 -3. `stun:stun.voipstunt.com` - VoIP服务 -4. `stun:stun.qq.com:3478` - 腾讯QQ -5. `stun:stun.l.google.com:19302` - Google(备用) - -## 🔧 高级配置 - -### 传输参数优化 - -在 `file-transfer.js` 中可调整以下参数: - -```javascript -const chunkSize = 65536; // 分块大小(64KB) -const transmissionDelay = 1; // 传输间隔(1ms) -const maxRetransmits = 3; // 最大重传次数 -const maxPacketLifeTime = 3000; // 数据包最大生存时间 -``` - -### 连接超时设置 - -在 `webrtc-connection.js` 中可调整: - -```javascript -const connectionTimeout = 60000; // 连接超时(60秒) -``` - -## 🐛 故障排除 - -### 常见问题 - -1. **P2P连接失败** - - 检查防火墙设置 - - 确认浏览器支持WebRTC - - 尝试使用不同的STUN服务器 - -2. **文件传输中断** - - 检查网络连接稳定性 - - 避免浏览器标签页切换到后台 - - 大文件传输建议分批进行 - -3. **取件码无效** - - 确认取件码输入正确(6位大写字母数字) - - 检查房间是否已过期(默认1小时) - - 确认发送方仍在线 - -### 调试模式 - -打开浏览器开发者工具(F12),查看Console标签页获取详细日志信息。 - -## 🚀 生产部署 - -### Nginx配置 - -```nginx -server { - listen 80; - server_name your-domain.com; - - location / { - proxy_pass http://localhost:8080; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /ws { - proxy_pass http://localhost:8080; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } -} -``` - -### systemd服务 - -创建 `/etc/systemd/system/chuan.service`: - -```ini -[Unit] -Description=Chuan P2P File Transfer Service -After=network.target - -[Service] -Type=simple -User=www-data -WorkingDirectory=/opt/chuan -ExecStart=/opt/chuan/bin/chuan -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -``` - -启动服务: -```bash -sudo systemctl enable chuan -sudo systemctl start chuan -``` - -## 🤝 贡献指南 - -1. Fork 项目 -2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) -3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) -4. 推送到分支 (`git push origin feature/AmazingFeature`) -5. 开启 Pull Request - -## 📝 开发计划 - -### v1.1 计划功能 -- [ ] 文件传输加密增强 -- [ ] 传输速度优化 -- [ ] 移动端适配改进 -- [ ] 批量文件操作 - -### v1.2 计划功能 -- [ ] 用户认证系统 -- [ ] 传输历史记录 -- [ ] 文件预览功能 -- [ ] API接口开放 - -### v2.0 计划功能 -- [ ] 视频通话功能完善 -- [ ] 屏幕共享支持 -- [ ] 多人会议室 -- [ ] 云存储集成 - -## 📄 许可证 - -本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情 - -## 🙏 致谢 - -- [WebRTC](https://webrtc.org/) - 实时通信技术 -- [Go](https://golang.org/) - 后端开发语言 -- [Tailwind CSS](https://tailwindcss.com/) - UI样式框架 -- [Chi Router](https://go-chi.io/) - Go HTTP路由库 - -## 📞 联系方式 - -- 项目主页:[GitHub Repository] -- 问题反馈:[GitHub Issues] --- -⭐ 如果这个项目对你有帮助,请给它一个星标! +⭐ 觉得有用请给个星标! diff --git a/chuan-next/next.config.js b/chuan-next/next.config.js new file mode 100644 index 0000000..b578210 --- /dev/null +++ b/chuan-next/next.config.js @@ -0,0 +1,40 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + // 环境变量配置 + env: { + GO_BACKEND_URL: process.env.GO_BACKEND_URL, + }, + + // 公共运行时配置 + publicRuntimeConfig: { + apiBaseUrl: process.env.NEXT_PUBLIC_API_BASE_URL, + wsUrl: process.env.NEXT_PUBLIC_WS_URL, + }, + + // 服务器端运行时配置 + serverRuntimeConfig: { + goBackendUrl: process.env.GO_BACKEND_URL, + }, + + // 重写规则 - 可选,用于代理API请求 + async rewrites() { + return [ + { + source: '/api/proxy/:path*', + destination: `${process.env.GO_BACKEND_URL}/api/:path*`, + }, + ] + }, + + // 输出配置 + output: 'standalone', + + // 实验性功能 + experimental: { + serverActions: { + allowedOrigins: ['localhost:3000', 'localhost:8080'], + }, + }, +} + +module.exports = nextConfig diff --git a/chuan-next/package.json b/chuan-next/package.json index 7c52b25..e8ebc58 100644 --- a/chuan-next/package.json +++ b/chuan-next/package.json @@ -4,9 +4,15 @@ "private": true, "scripts": { "dev": "next dev --turbopack", + "dev:prod": "NODE_ENV=production next dev --turbopack", "build": "next build", + "build:dev": "NODE_ENV=development next build", + "build:prod": "NODE_ENV=production next build", "start": "next start", - "lint": "next lint" + "start:dev": "NODE_ENV=development next start", + "start:prod": "NODE_ENV=production next start", + "lint": "next lint", + "env:check": "node -e \"console.log('Environment:', process.env.NODE_ENV); console.log('GO_BACKEND_URL:', process.env.GO_BACKEND_URL);\"" }, "dependencies": { "@radix-ui/react-avatar": "^1.1.10", diff --git a/chuan-next/src/app/HomePage-new.tsx b/chuan-next/src/app/HomePage-new.tsx index 39a3bce..c46b029 100644 --- a/chuan-next/src/app/HomePage-new.tsx +++ b/chuan-next/src/app/HomePage-new.tsx @@ -3,6 +3,8 @@ import { useState, useEffect, useCallback } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; import Hero from '@/components/Hero'; import FileTransfer from '@/components/FileTransfer'; import TextTransfer from '@/components/TextTransfer'; @@ -31,6 +33,10 @@ export default function HomePage() { // URL参数管理 const [activeTab, setActiveTab] = useState<'file' | 'text' | 'desktop'>('file'); + // 确认对话框状态 + const [showConfirmDialog, setShowConfirmDialog] = useState(false); + const [pendingTabSwitch, setPendingTabSwitch] = useState(''); + // 从URL参数中获取初始状态 useEffect(() => { const type = searchParams.get('type') as 'file' | 'text' | 'desktop'; @@ -81,49 +87,37 @@ export default function HomePage() { const hasActiveConnection = isConnected || pickupCode || isConnecting; if (hasActiveConnection && value !== activeTab) { - // 如果已有活跃连接且要切换到不同的tab,在新窗口打开 - const currentUrl = window.location.origin + window.location.pathname; - const newUrl = `${currentUrl}?type=${value}`; - - // 在新标签页打开 - window.open(newUrl, '_blank'); - - // 给出提示 - let currentMode = ''; - let targetMode = ''; - - switch (activeTab) { - case 'file': - currentMode = '文件传输'; - break; - case 'text': - currentMode = '文字传输'; - break; - case 'desktop': - currentMode = '桌面共享'; - break; - } - - switch (value) { - case 'file': - targetMode = '文件传输'; - break; - case 'text': - targetMode = '文字传输'; - break; - case 'desktop': - targetMode = '桌面共享'; - break; - } - - showNotification(`当前${currentMode}会话进行中,已在新标签页打开${targetMode}`, 'info'); + // 如果已有活跃连接且要切换到不同的tab,显示确认对话框 + setPendingTabSwitch(value); + setShowConfirmDialog(true); return; } // 如果没有活跃连接,正常切换 setActiveTab(value as 'file' | 'text' | 'desktop'); updateUrlParams(value); - }, [updateUrlParams, isConnected, pickupCode, isConnecting, activeTab, showNotification]); + }, [updateUrlParams, isConnected, pickupCode, isConnecting, activeTab]); + + // 确认切换tab + const confirmTabSwitch = useCallback(() => { + if (pendingTabSwitch) { + const currentUrl = window.location.origin + window.location.pathname; + const newUrl = `${currentUrl}?type=${pendingTabSwitch}`; + + // 在新标签页打开 + window.open(newUrl, '_blank'); + + // 关闭对话框并清理状态 + setShowConfirmDialog(false); + setPendingTabSwitch(''); + } + }, [pendingTabSwitch]); + + // 取消切换tab + const cancelTabSwitch = useCallback(() => { + setShowConfirmDialog(false); + setPendingTabSwitch(''); + }, []); // 初始化文件传输 const initFileTransfer = useCallback((fileInfo: any) => { @@ -811,6 +805,55 @@ export default function HomePage() {
+ + {/* 确认对话框 */} + + + + 切换传输模式 + + {(() => { + let currentMode = ''; + let targetMode = ''; + + switch (activeTab) { + case 'file': + currentMode = '文件传输'; + break; + case 'text': + currentMode = '文字传输'; + break; + case 'desktop': + currentMode = '桌面共享'; + break; + } + + switch (pendingTabSwitch) { + case 'file': + targetMode = '文件传输'; + break; + case 'text': + targetMode = '文字传输'; + break; + case 'desktop': + targetMode = '桌面共享'; + break; + } + + return `当前${currentMode}会话进行中,是否要在新标签页中打开${targetMode}?`; + })()} + + + + + + + + ); } diff --git a/chuan-next/src/app/api/create-room/route.ts b/chuan-next/src/app/api/create-room/route.ts index fbd9db0..4f7e341 100644 --- a/chuan-next/src/app/api/create-room/route.ts +++ b/chuan-next/src/app/api/create-room/route.ts @@ -1,13 +1,15 @@ import { NextRequest, NextResponse } from 'next/server'; - -const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080'; +import { getBackendUrl } from '@/lib/config'; export async function POST(request: NextRequest) { try { const body = await request.json(); + // 使用配置管理获取后端URL + const backendUrl = getBackendUrl('/api/create-room'); + // 转发请求到Go后端 - const response = await fetch(`${GO_BACKEND_URL}/api/create-room`, { + const response = await fetch(backendUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/chuan-next/src/app/api/create-text-room/route.ts b/chuan-next/src/app/api/create-text-room/route.ts index 4e19a15..45ebd54 100644 --- a/chuan-next/src/app/api/create-text-room/route.ts +++ b/chuan-next/src/app/api/create-text-room/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; +import { getBackendUrl } from '@/lib/config'; export async function POST(req: NextRequest) { try { @@ -13,7 +14,7 @@ export async function POST(req: NextRequest) { } // 调用后端API创建文字传输房间 - const response = await fetch('http://localhost:8080/api/create-text-room', { + const response = await fetch(getBackendUrl('/api/create-text-room'), { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/chuan-next/src/app/api/room-info/route.ts b/chuan-next/src/app/api/room-info/route.ts index 081e3bd..50d9bfc 100644 --- a/chuan-next/src/app/api/room-info/route.ts +++ b/chuan-next/src/app/api/room-info/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; - -const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080'; +import { getBackendUrl } from '@/lib/config'; export async function GET(request: NextRequest) { try { @@ -15,7 +14,7 @@ export async function GET(request: NextRequest) { } // 转发请求到Go后端 - const response = await fetch(`${GO_BACKEND_URL}/api/room-info?code=${code}`, { + const response = await fetch(getBackendUrl(`/api/room-info?code=${code}`), { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/chuan-next/src/components/Hero.tsx b/chuan-next/src/components/Hero.tsx index 0fefb48..85533d4 100644 --- a/chuan-next/src/components/Hero.tsx +++ b/chuan-next/src/components/Hero.tsx @@ -6,25 +6,35 @@ import { Github } from 'lucide-react'; export default function Hero() { return (
-
-

- 文件快传 -

- - - 开源 - -
-

+

+ 文件快传 +

+

安全、快速、简单的传输服务
支持文件、文字、桌面共享 - 无需注册,即传即用

+ + {/* GitHub开源链接 */} +
+ + + 开源项目 + + + https://github.com/MatrixSeven/file-transfer-go + +
); } diff --git a/chuan-next/src/components/ui/dialog.tsx b/chuan-next/src/components/ui/dialog.tsx new file mode 100644 index 0000000..01ff19c --- /dev/null +++ b/chuan-next/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/chuan-next/src/lib/config.ts b/chuan-next/src/lib/config.ts new file mode 100644 index 0000000..241c775 --- /dev/null +++ b/chuan-next/src/lib/config.ts @@ -0,0 +1,77 @@ +/** + * 环境配置管理 + */ + +export const config = { + // 环境判断 + isDev: process.env.NODE_ENV === 'development', + isProd: process.env.NODE_ENV === 'production', + + // API配置 + api: { + // 后端API地址 (服务器端使用) + backendUrl: process.env.GO_BACKEND_URL || 'http://localhost:8080', + + // 前端API基础URL (客户端使用) + baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', + + // WebSocket地址 + wsUrl: process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8080/ws', + }, + + // 超时配置 + timeout: { + api: 30000, // 30秒 + ws: 60000, // 60秒 + }, + + // 重试配置 + retry: { + max: 3, + delay: 1000, + }, +} + +/** + * 获取后端API完整URL + * @param path API路径 + * @returns 完整的API URL + */ +export function getBackendUrl(path: string): string { + const baseUrl = config.api.backendUrl.replace(/\/$/, '') + const apiPath = path.startsWith('/') ? path : `/${path}` + return `${baseUrl}${apiPath}` +} + +/** + * 获取前端API完整URL + * @param path API路径 + * @returns 完整的API URL + */ +export function getApiUrl(path: string): string { + const baseUrl = config.api.baseUrl.replace(/\/$/, '') + const apiPath = path.startsWith('/') ? path : `/${path}` + return `${baseUrl}${apiPath}` +} + +/** + * 获取WebSocket URL + * @returns WebSocket连接地址 + */ +export function getWsUrl(): string { + return config.api.wsUrl +} + +/** + * 环境配置调试信息 + */ +export function getEnvInfo() { + return { + environment: process.env.NODE_ENV, + backendUrl: config.api.backendUrl, + baseUrl: config.api.baseUrl, + wsUrl: config.api.wsUrl, + isDev: config.isDev, + isProd: config.isProd, + } +} diff --git a/deploy.sh b/deploy.sh deleted file mode 100755 index 39ce19a..0000000 --- a/deploy.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash - -# 文件传输系统部署脚本 -# 使用方法: ./deploy.sh [环境] -# 环境选项: dev, staging, production - -set -e - -# 颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 日志函数 -log_info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -log_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# 环境变量 -ENV=${1:-dev} -APP_NAME="chuan" -DOCKER_IMAGE="${APP_NAME}:${ENV}" -COMPOSE_FILE="docker-compose.yml" - -# 检查Docker和Docker Compose -check_dependencies() { - log_info "检查依赖..." - - if ! command -v docker &> /dev/null; then - log_error "Docker未安装,请先安装Docker" - exit 1 - fi - - if ! command -v docker-compose &> /dev/null; then - log_error "Docker Compose未安装,请先安装Docker Compose" - exit 1 - fi - - log_info "依赖检查完成" -} - -# 构建应用 -build_app() { - log_info "构建应用..." - - # 清理旧的构建 - docker-compose down --remove-orphans - docker system prune -f - - # 构建新镜像 - docker-compose build --no-cache - - log_info "应用构建完成" -} - -# 生成SSL证书(开发环境) -generate_ssl_cert() { - if [ "$ENV" = "dev" ]; then - log_info "生成开发环境SSL证书..." - - mkdir -p ssl - - if [ ! -f ssl/cert.pem ] || [ ! -f ssl/key.pem ]; then - openssl req -x509 -newkey rsa:4096 -keyout ssl/key.pem -out ssl/cert.pem -days 365 -nodes \ - -subj "/C=CN/ST=Beijing/L=Beijing/O=Chuan/OU=Dev/CN=localhost" - log_info "SSL证书生成完成" - else - log_info "SSL证书已存在,跳过生成" - fi - fi -} - -# 部署应用 -deploy_app() { - log_info "部署应用到${ENV}环境..." - - # 根据环境选择不同的配置 - case $ENV in - "dev") - export COMPOSE_FILE="docker-compose.yml" - ;; - "staging") - export COMPOSE_FILE="docker-compose.staging.yml" - ;; - "production") - export COMPOSE_FILE="docker-compose.prod.yml" - ;; - *) - log_error "未知环境: $ENV" - exit 1 - ;; - esac - - # 启动服务 - docker-compose up -d - - # 等待服务启动 - log_info "等待服务启动..." - sleep 10 - - # 健康检查 - if curl -f http://localhost:8080/health > /dev/null 2>&1; then - log_info "应用健康检查通过" - else - log_warn "应用健康检查失败,请检查日志" - fi - - log_info "部署完成" -} - -# 显示服务状态 -show_status() { - log_info "服务状态:" - docker-compose ps - - log_info "服务日志(最近20行):" - docker-compose logs --tail=20 -} - -# 备份数据 -backup_data() { - log_info "备份数据..." - - BACKUP_DIR="backup/$(date +%Y%m%d_%H%M%S)" - mkdir -p "$BACKUP_DIR" - - # 备份上传文件 - if [ -d "uploads" ]; then - cp -r uploads "$BACKUP_DIR/" - log_info "上传文件已备份到 $BACKUP_DIR/uploads" - fi - - # 备份Redis数据 - docker-compose exec -T redis redis-cli BGSAVE - docker cp $(docker-compose ps -q redis):/data/dump.rdb "$BACKUP_DIR/" - log_info "Redis数据已备份到 $BACKUP_DIR/dump.rdb" - - log_info "数据备份完成: $BACKUP_DIR" -} - -# 恢复数据 -restore_data() { - BACKUP_DIR=$2 - - if [ -z "$BACKUP_DIR" ]; then - log_error "请指定备份目录" - exit 1 - fi - - if [ ! -d "$BACKUP_DIR" ]; then - log_error "备份目录不存在: $BACKUP_DIR" - exit 1 - fi - - log_info "从 $BACKUP_DIR 恢复数据..." - - # 恢复上传文件 - if [ -d "$BACKUP_DIR/uploads" ]; then - rm -rf uploads/* - cp -r "$BACKUP_DIR/uploads/"* uploads/ - log_info "上传文件已恢复" - fi - - # 恢复Redis数据 - if [ -f "$BACKUP_DIR/dump.rdb" ]; then - docker-compose stop redis - docker cp "$BACKUP_DIR/dump.rdb" $(docker-compose ps -q redis):/data/ - docker-compose start redis - log_info "Redis数据已恢复" - fi - - log_info "数据恢复完成" -} - -# 清理资源 -cleanup() { - log_info "清理资源..." - - docker-compose down --volumes --remove-orphans - docker system prune -af - docker volume prune -f - - log_info "清理完成" -} - -# 显示帮助信息 -show_help() { - echo "文件传输系统部署脚本" - echo "" - echo "使用方法:" - echo " $0 [命令] [环境/参数]" - echo "" - echo "命令:" - echo " deploy [env] - 部署应用 (环境: dev, staging, production)" - echo " build - 构建应用" - echo " status - 显示服务状态" - echo " backup - 备份数据" - echo " restore [dir] - 恢复数据" - echo " cleanup - 清理资源" - echo " help - 显示帮助信息" - echo "" - echo "示例:" - echo " $0 deploy dev # 部署到开发环境" - echo " $0 deploy production # 部署到生产环境" - echo " $0 backup # 备份数据" - echo " $0 restore backup/20241128_120000 # 恢复数据" -} - -# 主函数 -main() { - case ${1:-deploy} in - "deploy") - check_dependencies - generate_ssl_cert - build_app - deploy_app - show_status - ;; - "build") - check_dependencies - build_app - ;; - "status") - show_status - ;; - "backup") - backup_data - ;; - "restore") - restore_data $@ - ;; - "cleanup") - cleanup - ;; - "help"|"-h"|"--help") - show_help - ;; - *) - log_error "未知命令: $1" - show_help - exit 1 - ;; - esac -} - -# 执行主函数 -main $@ diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 8f0d688..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,57 +0,0 @@ -version: '3.8' - -services: - # 主应用服务 - chuan: - build: - context: . - dockerfile: Dockerfile - ports: - - "8080:8080" - volumes: - - ./uploads:/root/uploads - - ./logs:/root/logs - environment: - - TZ=Asia/Shanghai - - PORT=8080 - - REDIS_URL=redis://redis:6379 - depends_on: - - redis - restart: unless-stopped - networks: - - chuan-network - - # Redis服务(用于存储取件码) - redis: - image: redis:7-alpine - ports: - - "6379:6379" - volumes: - - redis_data:/data - command: redis-server --appendonly yes --requirepass "" - restart: unless-stopped - networks: - - chuan-network - - # Nginx反向代理 - nginx: - image: nginx:alpine - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./ssl:/etc/nginx/ssl - - ./uploads:/var/www/uploads - depends_on: - - chuan - restart: unless-stopped - networks: - - chuan-network - -volumes: - redis_data: - -networks: - chuan-network: - driver: bridge diff --git a/img.png b/img.png new file mode 100644 index 0000000..39e773e Binary files /dev/null and b/img.png differ diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index ba3306e..0000000 --- a/nginx.conf +++ /dev/null @@ -1,181 +0,0 @@ -user nginx; -worker_processes auto; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; - use epoll; - multi_accept on; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - # 日志格式 - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - # 基本设置 - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - types_hash_max_size 2048; - server_tokens off; - - # Gzip压缩 - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_types - text/plain - text/css - text/xml - text/javascript - application/javascript - application/xml+rss - application/json; - - # 文件上传限制 - client_max_body_size 64G; - client_body_timeout 60s; - client_header_timeout 60s; - - # 缓存设置 - open_file_cache max=10000 inactive=5m; - open_file_cache_valid 2m; - open_file_cache_min_uses 1; - open_file_cache_errors on; - - # 上游服务器 - upstream chuan_backend { - server chuan:8080; - keepalive 32; - } - - # HTTP服务器(重定向到HTTPS) - server { - listen 80; - server_name _; - return 301 https://$server_name$request_uri; - } - - # HTTPS服务器 - server { - listen 443 ssl http2; - server_name _; - - # SSL配置 - ssl_certificate /etc/nginx/ssl/cert.pem; - ssl_certificate_key /etc/nginx/ssl/key.pem; - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_session_tickets off; - - # 现代SSL配置 - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; - ssl_prefer_server_ciphers off; - - # 安全头 - add_header Strict-Transport-Security "max-age=63072000" always; - add_header X-Frame-Options DENY; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - - # 静态文件缓存 - location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - add_header Vary Accept-Encoding; - access_log off; - } - - # 上传文件服务 - location /uploads/ { - alias /var/www/uploads/; - expires 24h; - add_header Cache-Control "public"; - - # 安全设置 - add_header X-Content-Type-Options nosniff; - add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline';"; - - # 限制访问 - valid_referers none blocked server_names; - if ($invalid_referer) { - return 403; - } - } - - # WebSocket代理 - location /ws/ { - proxy_pass http://chuan_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 86400; - } - - # API代理 - location /api/ { - proxy_pass http://chuan_backend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # 超时设置 - proxy_connect_timeout 60s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - # 主应用代理 - location / { - proxy_pass http://chuan_backend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # 缓存设置 - proxy_buffering on; - proxy_buffer_size 4k; - proxy_buffers 8 4k; - - # 超时设置 - proxy_connect_timeout 30s; - proxy_send_timeout 30s; - proxy_read_timeout 30s; - } - - # 健康检查 - location /health { - access_log off; - return 200 "healthy\n"; - add_header Content-Type text/plain; - } - - # 错误页面 - error_page 404 /404.html; - error_page 500 502 503 504 /50x.html; - - location = /404.html { - root /usr/share/nginx/html; - } - - location = /50x.html { - root /usr/share/nginx/html; - } - } -}