remove redis

This commit is contained in:
朱潮 2025-08-10 13:02:43 +08:00
parent 4985344911
commit 989b9484a7
10 changed files with 7 additions and 1059 deletions

View File

@ -41,19 +41,12 @@ cp .env.example .env
# 基础部署
docker-compose up -d
# 包含Traefik反向代理 (生产环境推荐)
docker-compose --profile traefik up -d
# 包含Redis缓存
docker-compose --profile redis up -d
```
**访问服务:**
- 🌐 Web界面: http://localhost:8000
- 📖 API文档: http://localhost:8000/docs
- API信息: http://localhost:8000/api
- 如果启用了Traefik: http://traefik.localhost:8080
### 方式3: 本地运行
@ -203,20 +196,11 @@ python cli.py info ABCD1234
### 生产环境
1. 使用Traefik反向代理
```bash
docker-compose --profile traefik up -d
```
2. 配置SSL证书
- 修改`.env`中的`ACME_EMAIL`
- 确保域名正确指向服务器
3. 数据持久化:
1. 数据持久化:
- 上传文件会保存在`./data/uploads`
- 可以备份该目录
4. 监控和日志:
2. 监控和日志:
- 日志输出到`./data/logs`
- 支持健康检查
@ -224,9 +208,7 @@ docker-compose --profile traefik up -d
1. 设置合适的文件大小限制
2. 定期清理过期文件
3. 使用HTTPS生产环境
4. 限制访问IP如需要
5. 设置反向代理限流
3. 限制访问IP如需要
## 开发

365
cli.py
View File

@ -1,365 +0,0 @@
#!/usr/bin/env python3
"""
文件传输服务 - 命令行工具
支持文件上传文本分享和下载功能
"""
import os
import sys
import json
from pathlib import Path
from typing import Optional
import click
import httpx
from rich.console import Console
from rich.table import Table
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.panel import Panel
from rich.prompt import Prompt
console = Console()
# 默认服务器配置
DEFAULT_SERVER = "http://localhost:8000"
SERVER_URL = os.getenv("FILESHARE_SERVER", DEFAULT_SERVER)
class FileShareClient:
def __init__(self, server_url: str = SERVER_URL):
self.server_url = server_url.rstrip("/")
self.client = httpx.Client(timeout=60.0)
def upload_file(self, file_path: Path) -> dict:
"""上传文件"""
if not file_path.exists():
raise FileNotFoundError(f"文件不存在: {file_path}")
with open(file_path, "rb") as f:
files = {"file": (file_path.name, f, "application/octet-stream")}
response = self.client.post(f"{self.server_url}/api/upload", files=files)
if response.status_code == 200:
return response.json()
else:
raise Exception(f"上传失败: {response.status_code} - {response.text}")
def share_text(self, content: str, filename: str = "shared_text.txt") -> dict:
"""分享文本"""
data = {"content": content, "filename": filename}
response = self.client.post(f"{self.server_url}/api/share-text", json=data)
if response.status_code == 200:
return response.json()
else:
raise Exception(f"分享失败: {response.status_code} - {response.text}")
def download_file(self, code: str, output_dir: Path = Path(".")) -> Path:
"""下载文件"""
# 先获取文件信息
info_response = self.client.get(f"{self.server_url}/api/info/{code}")
if info_response.status_code != 200:
raise Exception(f"获取文件信息失败: {info_response.status_code}")
file_info = info_response.json()
filename = file_info["filename"]
# 下载文件
response = self.client.get(f"{self.server_url}/api/download/{code}")
if response.status_code != 200:
raise Exception(f"下载失败: {response.status_code} - {response.text}")
# 保存文件
output_path = output_dir / filename
# 如果文件存在,添加序号
counter = 1
original_path = output_path
while output_path.exists():
stem = original_path.stem
suffix = original_path.suffix
output_path = output_dir / f"{stem}_{counter}{suffix}"
counter += 1
with open(output_path, "wb") as f:
f.write(response.content)
return output_path
def get_info(self, code: str) -> dict:
"""获取分享信息"""
response = self.client.get(f"{self.server_url}/api/info/{code}")
if response.status_code == 200:
return response.json()
else:
raise Exception(f"获取信息失败: {response.status_code} - {response.text}")
def list_shares(self) -> dict:
"""列出所有分享"""
response = self.client.get(f"{self.server_url}/api/shares")
if response.status_code == 200:
return response.json()
else:
raise Exception(f"获取列表失败: {response.status_code} - {response.text}")
@click.group()
@click.option("--server", default=SERVER_URL, help="服务器地址")
@click.pass_context
def cli(ctx, server):
"""文件传输服务命令行工具"""
ctx.ensure_object(dict)
ctx.obj['client'] = FileShareClient(server)
ctx.obj['server'] = server
@cli.command()
@click.argument("file_path", type=click.Path(exists=True, path_type=Path))
@click.pass_context
def upload(ctx, file_path: Path):
"""上传文件并获取分享码"""
client = ctx.obj['client']
try:
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
) as progress:
task = progress.add_task(f"上传文件 {file_path.name}...", total=None)
result = client.upload_file(file_path)
# 显示结果
panel = Panel.fit(
f"[green]✓ 文件上传成功![/green]\n\n"
f"[bold]分享码:[/bold] [yellow]{result['code']}[/yellow]\n"
f"[bold]过期时间:[/bold] {result['expires_at']}\n"
f"[bold]下载链接:[/bold] {ctx.obj['server']}{result['download_url']}",
title="上传成功",
border_style="green"
)
console.print(panel)
console.print(f"\n[dim]使用命令下载: [bold]python cli.py download {result['code']}[/bold][/dim]")
except Exception as e:
console.print(f"[red]❌ 上传失败: {e}[/red]")
sys.exit(1)
@cli.command()
@click.option("--text", "-t", help="要分享的文本内容")
@click.option("--file", "-f", "text_file", type=click.Path(exists=True, path_type=Path), help="要分享的文本文件")
@click.option("--filename", default="shared_text.txt", help="分享文件名")
@click.pass_context
def share_text(ctx, text: Optional[str], text_file: Optional[Path], filename: str):
"""分享文本内容"""
client = ctx.obj['client']
# 确定文本内容
if text_file:
try:
with open(text_file, "r", encoding="utf-8") as f:
content = f.read()
if not filename.endswith(".txt") and text_file.suffix:
filename = f"shared_{text_file.name}"
except Exception as e:
console.print(f"[red]❌ 读取文件失败: {e}[/red]")
sys.exit(1)
elif text:
content = text
else:
# 交互式输入
console.print("[blue]请输入要分享的文本内容按Ctrl+D或Ctrl+Z结束:[/blue]")
lines = []
try:
while True:
line = input()
lines.append(line)
except EOFError:
content = "\n".join(lines)
if not content.strip():
console.print("[red]❌ 文本内容不能为空[/red]")
sys.exit(1)
try:
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
) as progress:
task = progress.add_task("分享文本...", total=None)
result = client.share_text(content, filename)
# 显示结果
panel = Panel.fit(
f"[green]✓ 文本分享成功![/green]\n\n"
f"[bold]分享码:[/bold] [yellow]{result['code']}[/yellow]\n"
f"[bold]文件名:[/bold] {filename}\n"
f"[bold]过期时间:[/bold] {result['expires_at']}\n"
f"[bold]下载链接:[/bold] {ctx.obj['server']}{result['download_url']}",
title="分享成功",
border_style="green"
)
console.print(panel)
console.print(f"\n[dim]使用命令下载: [bold]python cli.py download {result['code']}[/bold][/dim]")
except Exception as e:
console.print(f"[red]❌ 分享失败: {e}[/red]")
sys.exit(1)
@cli.command()
@click.argument("code")
@click.option("--output", "-o", type=click.Path(path_type=Path), default=".", help="输出目录")
@click.pass_context
def download(ctx, code: str, output: Path):
"""通过分享码下载文件"""
client = ctx.obj['client']
try:
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
) as progress:
task = progress.add_task(f"下载分享码 {code}...", total=None)
output_path = client.download_file(code, output)
# 显示结果
file_size = output_path.stat().st_size
panel = Panel.fit(
f"[green]✓ 文件下载成功![/green]\n\n"
f"[bold]文件路径:[/bold] {output_path.absolute()}\n"
f"[bold]文件大小:[/bold] {file_size:,} 字节",
title="下载成功",
border_style="green"
)
console.print(panel)
except Exception as e:
console.print(f"[red]❌ 下载失败: {e}[/red]")
sys.exit(1)
@cli.command()
@click.argument("code")
@click.pass_context
def info(ctx, code: str):
"""获取分享信息"""
client = ctx.obj['client']
try:
result = client.get_info(code)
# 计算文件大小显示
size = result['size']
if size < 1024:
size_str = f"{size} B"
elif size < 1024 * 1024:
size_str = f"{size / 1024:.1f} KB"
else:
size_str = f"{size / (1024 * 1024):.1f} MB"
# 显示信息
status = "[red]已过期[/red]" if result['is_expired'] else "[green]有效[/green]"
panel = Panel.fit(
f"[bold]分享码:[/bold] [yellow]{result['code']}[/yellow]\n"
f"[bold]文件名:[/bold] {result['filename']}\n"
f"[bold]文件类型:[/bold] {result['file_type']}\n"
f"[bold]文件大小:[/bold] {size_str}\n"
f"[bold]创建时间:[/bold] {result['created_at']}\n"
f"[bold]过期时间:[/bold] {result['expires_at']}\n"
f"[bold]状态:[/bold] {status}",
title="分享信息",
border_style="blue"
)
console.print(panel)
except Exception as e:
console.print(f"[red]❌ 获取信息失败: {e}[/red]")
sys.exit(1)
@cli.command()
@click.pass_context
def list(ctx):
"""列出所有分享"""
client = ctx.obj['client']
try:
result = client.list_shares()
if result['total'] == 0:
console.print("[yellow]没有找到任何分享[/yellow]")
return
# 创建表格
table = Table(title=f"所有分享 (共 {result['total']} 个)")
table.add_column("分享码", style="yellow", no_wrap=True)
table.add_column("文件名", style="blue")
table.add_column("类型", style="green")
table.add_column("大小", justify="right")
table.add_column("剩余时间", justify="center")
table.add_column("状态", justify="center")
for share in result['shares']:
# 计算文件大小显示
size = share['size']
if size < 1024:
size_str = f"{size}B"
elif size < 1024 * 1024:
size_str = f"{size / 1024:.1f}K"
else:
size_str = f"{size / (1024 * 1024):.1f}M"
# 剩余时间
remaining = share.get('remaining_minutes', 0)
if remaining > 0:
remaining_str = f"{remaining}分钟"
else:
remaining_str = "已过期"
# 状态
status = "[red]过期[/red]" if share['is_expired'] else "[green]有效[/green]"
table.add_row(
share['code'],
share['filename'][:30] + ("..." if len(share['filename']) > 30 else ""),
share['file_type'].split('/')[-1] if '/' in share['file_type'] else share['file_type'],
size_str,
remaining_str,
status
)
console.print(table)
console.print(f"\n[dim]使用 'python cli.py info <分享码>' 查看详细信息[/dim]")
except Exception as e:
console.print(f"[red]❌ 获取列表失败: {e}[/red]")
sys.exit(1)
@cli.command()
@click.pass_context
def server_info(ctx):
"""获取服务器信息"""
client = ctx.obj['client']
try:
response = client.client.get(f"{client.server_url}/")
if response.status_code == 200:
info = response.json()
panel = Panel.fit(
f"[bold]服务名称:[/bold] {info['service']}\n"
f"[bold]版本:[/bold] {info['version']}\n"
f"[bold]服务器地址:[/bold] {client.server_url}\n\n"
f"[bold]功能特性:[/bold]\n" +
"\n".join([f"{feature}" for feature in info['features']]),
title="服务器信息",
border_style="blue"
)
console.print(panel)
else:
console.print(f"[red]❌ 无法连接到服务器: {response.status_code}[/red]")
except Exception as e:
console.print(f"[red]❌ 连接服务器失败: {e}[/red]")
console.print(f"[dim]请确保服务器正在运行: {client.server_url}[/dim]")
sys.exit(1)
if __name__ == "__main__":
cli()

View File

@ -1,71 +0,0 @@
#!/bin/bash
# curl命令示例脚本 - 展示文件传输服务的基本用法
SERVER_URL="http://localhost:8000"
echo "📡 文件传输服务 - curl命令示例"
echo "================================="
echo
# 检查服务器是否运行
echo "🔍 检查服务器状态..."
if curl -s "$SERVER_URL/api" > /dev/null; then
echo "✅ 服务器运行正常"
else
echo "❌ 无法连接到服务器"
echo "💡 请先启动服务: python app.py"
exit 1
fi
echo
echo "🌐 Web界面: $SERVER_URL"
echo "📖 curl教程: $SERVER_URL/curl"
echo
# 显示使用示例
echo "📝 使用示例:"
echo
echo "1. 📤 上传文件:"
echo " curl -X POST -F \"file=@文件路径\" $SERVER_URL/api/upload"
echo
echo " 示例:"
echo " curl -X POST -F \"file=@photo.jpg\" $SERVER_URL/api/upload"
echo
echo "2. 📝 分享文本(超级简单):"
echo " curl -X POST --data \"你的文本\" $SERVER_URL/api/text"
echo
echo " 示例:"
echo " curl -X POST --data \"Hello World!\" $SERVER_URL/api/text"
echo
echo " 🔧 指定文件名:"
echo " curl -X POST -F \"content=Hello World!\" -F \"filename=hello.txt\" $SERVER_URL/api/share-text-form"
echo
echo "3. ⬇️ 下载文件:"
echo " curl -O -J $SERVER_URL/api/download/分享码"
echo
echo " 示例:"
echo " curl -O -J $SERVER_URL/api/download/AB12CD34"
echo
echo "4. 查看文件信息:"
echo " curl $SERVER_URL/api/info/分享码"
echo
echo " 示例:"
echo " curl $SERVER_URL/api/info/AB12CD34"
echo
echo "5. 📊 列出所有分享:"
echo " curl $SERVER_URL/api/shares"
echo
echo "================================="
echo "💡 提示:"
echo " - 无需安装任何额外工具"
echo " - 分享码为8位大写字母+数字"
echo " - 文件15分钟后自动过期"
echo " - 最大支持100MB文件"
echo
echo "📖 查看完整教程: $SERVER_URL/curl"

102
demo.sh
View File

@ -1,102 +0,0 @@
#!/bin/bash
# 文件传输服务演示脚本
SERVER_URL="http://localhost:8000"
echo "🎉 文件传输服务演示"
echo "===================="
echo
# 检查服务器
echo "🔍 检查服务器状态..."
if ! curl -s "$SERVER_URL/api" > /dev/null; then
echo "❌ 服务器未启动,请先运行: python app.py"
exit 1
fi
echo "✅ 服务器运行正常"
echo
# 演示1: 分享文本(超级简单方式)
echo "📝 演示1: 分享文本(超级简单)"
echo "命令: curl -X POST --data \"Hello from demo!\" $SERVER_URL/api/text"
echo
result1=$(curl -s -X POST --data "Hello from demo!" "$SERVER_URL/api/text")
if echo "$result1" | grep -q '"code"'; then
code1=$(echo "$result1" | grep -o '"code":"[^"]*"' | cut -d'"' -f4)
echo "✅ 分享成功! 分享码: $code1"
echo "🔗 下载链接: $SERVER_URL/api/download/$code1"
else
echo "❌ 分享失败"
fi
echo
# 演示2: 创建临时文件并上传
echo "📤 演示2: 上传文件"
temp_file=$(mktemp)
echo "这是一个演示文件
创建时间: $(date)
文件内容测试" > "$temp_file"
echo "命令: curl -X POST -F \"file=@$temp_file\" $SERVER_URL/api/upload"
echo
result2=$(curl -s -X POST -F "file=@$temp_file" "$SERVER_URL/api/upload")
if echo "$result2" | grep -q '"code"'; then
code2=$(echo "$result2" | grep -o '"code":"[^"]*"' | cut -d'"' -f4)
echo "✅ 上传成功! 分享码: $code2"
echo "🔗 下载链接: $SERVER_URL/api/download/$code2"
else
echo "❌ 上传失败"
fi
rm -f "$temp_file"
echo
# 演示3: 表单方式分享文本
echo "📋 演示3: 表单方式分享文本(可指定文件名)"
echo "命令: curl -X POST -F \"content=#!/bin/bash
echo 'Hello Shell!'\" -F \"filename=demo.sh\" $SERVER_URL/api/share-text-form"
echo
result3=$(curl -s -X POST -F "content=#!/bin/bash
echo 'Hello Shell!'" -F "filename=demo.sh" "$SERVER_URL/api/share-text-form")
if echo "$result3" | grep -q '"code"'; then
code3=$(echo "$result3" | grep -o '"code":"[^"]*"' | cut -d'"' -f4)
echo "✅ 分享成功! 分享码: $code3"
echo "🔗 下载链接: $SERVER_URL/api/download/$code3"
else
echo "❌ 分享失败"
fi
echo
# 演示4: 查看所有分享
echo "📊 演示4: 查看所有分享"
echo "命令: curl $SERVER_URL/api/shares"
echo
shares_result=$(curl -s "$SERVER_URL/api/shares")
if echo "$shares_result" | grep -q '"total"'; then
total=$(echo "$shares_result" | grep -o '"total":[0-9]*' | cut -d':' -f2)
echo "✅ 当前共有 $total 个分享"
echo
echo "$shares_result" | python3 -m json.tool 2>/dev/null || echo "$shares_result"
else
echo "❌ 获取分享列表失败"
fi
echo
# 演示便捷函数
echo "🚀 演示5: 便捷函数使用"
echo "加载函数: source fileshare_functions.sh"
echo "然后就可以使用超级简单的命令:"
echo
echo " upload photo.jpg # 上传文件"
echo " share_text \"Hello World!\" # 分享文本"
echo " download AB12CD34 # 下载文件"
echo " info AB12CD34 # 查看信息"
echo " list_shares # 列出分享"
echo
echo "===================="
echo "🎉 演示完成!"
echo
echo "📖 查看完整教程: $SERVER_URL/curl"
echo "🌐 Web界面: $SERVER_URL"
echo "🧪 运行测试: ./verify_setup.sh"
echo "📝 查看示例: ./curl_examples.sh"

View File

@ -40,81 +40,6 @@ services:
cpus: '0.5'
memory: 256M
# Traefik 反向代理 (使用阿里云镜像)
traefik:
image: registry.cn-hangzhou.aliyuncs.com/acs/traefik:v3.0
container_name: fileshare-traefik
restart: unless-stopped
profiles:
- traefik
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL:-admin@localhost}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--log.level=INFO"
- "--accesslog=true"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Traefik dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/letsencrypt:/letsencrypt
- ./data/logs/traefik:/var/log/traefik
networks:
- fileshare-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_AUTH:-admin:$$2y$$10$$WQiE8P/7W8MZ0GKJYLgVAOUV8D5e6Y7s8rF8w1M9i6QjLqN/3rZ0G}"
# Redis缓存 (使用阿里云镜像)
redis:
image: registry.cn-hangzhou.aliyuncs.com/acs/redis:7-alpine
container_name: fileshare-redis
restart: unless-stopped
profiles:
- redis
ports:
- "6379:6379"
volumes:
- ./data/redis:/data
- ./config/redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- fileshare-network
command: redis-server /usr/local/etc/redis/redis.conf
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.2'
memory: 128M
# Nginx (可选,用于静态文件服务)
nginx:
image: registry.cn-hangzhou.aliyuncs.com/acs/nginx:alpine
container_name: fileshare-nginx
restart: unless-stopped
profiles:
- nginx
ports:
- "80:80"
volumes:
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
- ./data/uploads:/usr/share/nginx/html/uploads:ro
- ./data/logs/nginx:/var/log/nginx
networks:
- fileshare-network
depends_on:
- fileshare
networks:
fileshare-network:
@ -123,10 +48,3 @@ networks:
config:
- subnet: 172.20.0.0/16
volumes:
uploads:
driver: local
logs:
driver: local
redis_data:
driver: local

View File

@ -27,62 +27,9 @@ services:
timeout: 10s
retries: 3
start_period: 40s
labels:
- "traefik.enable=true"
- "traefik.http.routers.fileshare.rule=Host(`fileshare.localhost`)"
- "traefik.http.services.fileshare.loadbalancer.server.port=8000"
# 可选使用Traefik作为反向代理生产环境推荐
traefik:
image: traefik:v3.0
container_name: fileshare-traefik
restart: unless-stopped
profiles:
- traefik # 使用profile控制是否启动
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL:-admin@localhost}"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Traefik dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/letsencrypt:/letsencrypt
networks:
- fileshare-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.dashboard.service=api@internal"
# 可选Redis缓存用于集群部署时共享会话
redis:
image: redis:7-alpine
container_name: fileshare-redis
restart: unless-stopped
profiles:
- redis # 使用profile控制是否启动
ports:
- "6379:6379"
volumes:
- ./data/redis:/data
networks:
- fileshare-network
command: redis-server --appendonly yes
networks:
fileshare-network:
driver: bridge
volumes:
uploads:
driver: local
logs:
driver: local

View File

@ -1,127 +0,0 @@
#!/usr/bin/env python3
"""
路径修复脚本 - 确保所有文件路径正确
"""
import os
import re
from pathlib import Path
def fix_html_paths():
"""修复HTML中的静态资源路径"""
html_file = Path("static/index.html")
if not html_file.exists():
print(f"❌ 文件不存在: {html_file}")
return False
print("🔧 修复HTML中的静态资源路径...")
with open(html_file, 'r', encoding='utf-8') as f:
content = f.read()
# 修复CSS路径
content = re.sub(
r'<link rel="stylesheet" href="(?!/)([^"]+\.css)"',
r'<link rel="stylesheet" href="/static/\1"',
content
)
# 修复JS路径
content = re.sub(
r'<script src="(?!/)([^"]+\.js)"',
r'<script src="/static/\1"',
content
)
# 修复图片路径(如果有的话)
content = re.sub(
r'<img src="(?!/)(?!http)([^"]+\.(png|jpg|jpeg|gif|svg))"',
r'<img src="/static/\1"',
content
)
with open(html_file, 'w', encoding='utf-8') as f:
f.write(content)
print("✅ HTML路径修复完成")
return True
def check_file_structure():
"""检查文件结构是否正确"""
print("📁 检查文件结构...")
required_files = [
"app.py",
"cli.py",
"requirements.txt",
"static/index.html",
"static/style.css",
"static/app.js"
]
missing_files = []
for file_path in required_files:
if not Path(file_path).exists():
missing_files.append(file_path)
print(f"❌ 缺少文件: {file_path}")
else:
print(f"✅ 文件存在: {file_path}")
if missing_files:
print(f"\n⚠️ 缺少 {len(missing_files)} 个文件")
return False
else:
print("\n🎉 所有必需文件都存在")
return True
def create_missing_directories():
"""创建缺失的目录"""
print("📂 创建必需目录...")
directories = [
"static",
"uploads",
"data",
"data/uploads",
"data/logs"
]
for dir_path in directories:
Path(dir_path).mkdir(parents=True, exist_ok=True)
print(f"✅ 目录已确保存在: {dir_path}")
def main():
"""主函数"""
print("🔧 文件传输服务路径修复工具")
print("=" * 50)
# 检查当前目录
if not Path("app.py").exists():
print("❌ 请在fileshare目录中运行此脚本")
print("💡 cd fileshare && python fix_paths.py")
return False
# 创建目录
create_missing_directories()
# 检查文件结构
if not check_file_structure():
print("\n❌ 文件结构不完整,请检查")
return False
# 修复HTML路径
if not fix_html_paths():
return False
print("\n" + "=" * 50)
print("✅ 路径修复完成!")
print("\n🚀 现在可以启动服务:")
print(" python app.py")
print("\n🧪 或运行测试:")
print(" python test_server.py")
return True
if __name__ == "__main__":
main()

View File

@ -27,33 +27,11 @@ fi
# 创建数据目录
echo "📁 创建数据目录..."
mkdir -p data/uploads data/logs data/redis data/letsencrypt
mkdir -p data/uploads data/logs
# 选择启动方式
echo "请选择启动方式:"
echo "1) 基础模式 (仅文件传输服务)"
echo "2) 完整模式 (包含Traefik反向代理)"
echo "3) 集群模式 (包含Redis缓存)"
read -p "请输入选择 (1-3): " choice
case $choice in
1)
echo "🚀 启动基础模式..."
docker-compose up -d fileshare
;;
2)
echo "🚀 启动完整模式 (包含Traefik)..."
docker-compose --profile traefik up -d
;;
3)
echo "🚀 启动集群模式 (包含Redis)..."
docker-compose --profile redis up -d
;;
*)
echo "❌ 无效选择,使用基础模式启动..."
docker-compose up -d fileshare
;;
esac
# 启动服务
echo "🚀 启动文件传输服务..."
docker-compose up -d fileshare
echo
echo "⏳ 等待服务启动..."
@ -67,10 +45,6 @@ if docker-compose ps | grep -q "Up"; then
echo " - API服务: http://localhost:8000"
echo " - API文档: http://localhost:8000/docs"
if [ "$choice" = "2" ]; then
echo " - Traefik面板: http://traefik.localhost:8080"
fi
echo
echo "🔧 常用命令:"
echo " - 查看日志: docker-compose logs -f"

View File

@ -1,84 +0,0 @@
#!/usr/bin/env python3
"""
快速测试脚本 - 验证服务是否正常运行
"""
import requests
import time
import sys
def test_server():
"""测试服务器是否正常运行"""
base_url = "http://localhost:8000"
print("🧪 文件传输服务测试")
print("=" * 50)
# 测试首页
print("1. 测试首页...")
try:
response = requests.get(base_url, timeout=10)
if response.status_code == 200:
print("✅ 首页正常 (200)")
if "文件传输服务" in response.text:
print("✅ 页面内容正确")
else:
print("⚠️ 页面内容异常")
else:
print(f"❌ 首页异常 ({response.status_code})")
return False
except Exception as e:
print(f"❌ 无法连接到服务器: {e}")
print("💡 请确保服务器已启动: python app.py")
return False
# 测试静态文件
print("\n2. 测试静态文件...")
static_files = [
"/static/style.css",
"/static/app.js"
]
for file_path in static_files:
try:
response = requests.get(f"{base_url}{file_path}", timeout=5)
if response.status_code == 200:
print(f"{file_path} - 正常")
else:
print(f"{file_path} - 异常 ({response.status_code})")
except Exception as e:
print(f"{file_path} - 错误: {e}")
# 测试API信息
print("\n3. 测试API...")
try:
response = requests.get(f"{base_url}/api", timeout=5)
if response.status_code == 200:
data = response.json()
print("✅ API信息正常")
print(f" 服务: {data.get('service', 'N/A')}")
print(f" 版本: {data.get('version', 'N/A')}")
else:
print(f"❌ API异常 ({response.status_code})")
except Exception as e:
print(f"❌ API错误: {e}")
# 测试API文档
print("\n4. 测试API文档...")
try:
response = requests.get(f"{base_url}/docs", timeout=5)
if response.status_code == 200:
print("✅ API文档正常")
else:
print(f"❌ API文档异常 ({response.status_code})")
except Exception as e:
print(f"❌ API文档错误: {e}")
print("\n" + "=" * 50)
print("🎉 测试完成!")
print(f"🌐 访问地址: {base_url}")
return True
if __name__ == "__main__":
if not test_server():
sys.exit(1)

View File

@ -1,124 +0,0 @@
#!/bin/bash
# 服务验证脚本 - 检查服务是否正确设置和运行
echo "🔍 文件传输服务验证脚本"
echo "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "="
# 检查当前目录
if [ ! -f "app.py" ]; then
echo "❌ 请在fileshare目录中运行此脚本"
echo "💡 cd fileshare && ./verify_setup.sh"
exit 1
fi
echo "📂 检查文件结构..."
# 检查必需文件
files=("app.py" "cli.py" "requirements.txt" "static/index.html" "static/style.css" "static/app.js")
missing_files=()
for file in "${files[@]}"; do
if [ -f "$file" ]; then
echo "$file"
else
echo "❌ 缺少文件: $file"
missing_files+=("$file")
fi
done
if [ ${#missing_files[@]} -ne 0 ]; then
echo
echo "⚠️ 发现 ${#missing_files[@]} 个缺失文件,请先运行: python fix_paths.py"
exit 1
fi
echo
echo "🔧 检查Python依赖..."
# 检查Python是否安装
if ! command -v python &> /dev/null && ! command -v python3 &> /dev/null; then
echo "❌ Python 未安装"
exit 1
else
echo "✅ Python 已安装"
fi
# 检查pip是否安装
if ! command -v pip &> /dev/null && ! command -v pip3 &> /dev/null; then
echo "❌ pip 未安装"
exit 1
else
echo "✅ pip 已安装"
fi
# 检查虚拟环境(可选)
if [ -n "$VIRTUAL_ENV" ]; then
echo "✅ 当前在虚拟环境中: $(basename $VIRTUAL_ENV)"
else
echo " 未使用虚拟环境(可选)"
fi
echo
echo "📦 检查依赖包..."
# 检查主要依赖
python_cmd="python"
if command -v python3 &> /dev/null; then
python_cmd="python3"
fi
required_packages=("fastapi" "uvicorn" "httpx" "click" "rich")
missing_packages=()
for package in "${required_packages[@]}"; do
if $python_cmd -c "import $package" 2>/dev/null; then
echo "$package"
else
echo "❌ 缺少包: $package"
missing_packages+=("$package")
fi
done
if [ ${#missing_packages[@]} -ne 0 ]; then
echo
echo "⚠️ 发现 ${#missing_packages[@]} 个缺失依赖包,请运行:"
echo " pip install -r requirements.txt"
echo
fi
echo
echo "🚀 启动测试..."
# 检查端口是否被占用
if command -v lsof &> /dev/null; then
if lsof -i :8000 > /dev/null 2>&1; then
echo "⚠️ 端口 8000 已被占用"
echo " 当前占用进程:"
lsof -i :8000
echo
echo "💡 可以使用其他端口: PORT=8001 python app.py"
else
echo "✅ 端口 8000 可用"
fi
fi
# 提供启动建议
echo
echo "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "=" "="
echo "🎉 验证完成!"
echo
if [ ${#missing_files[@]} -eq 0 ] && [ ${#missing_packages[@]} -eq 0 ]; then
echo "✅ 所有检查都通过,可以启动服务了!"
echo
echo "🚀 推荐启动方式:"
echo " 1. 直接启动: python app.py"
echo " 2. 开发模式: uvicorn app:app --reload"
echo " 3. Docker启动: ./start.sh"
echo
echo "🌐 启动后访问: http://localhost:8000"
echo
echo "🧪 启动后可运行测试: python test_server.py"
else
echo "⚠️ 请先解决上述问题,然后重新运行此脚本"
fi