把不需要的测试文件全部删掉
This commit is contained in:
@@ -1,369 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 云盘应用 Docker 构建和部署脚本 (修复权限问题版本)
|
||||
# 用于构建生产环境的 Docker 镜像并部署到 Linux 环境
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="cloud-drive-backend"
|
||||
IMAGE_NAME="${APP_NAME}:latest"
|
||||
CONTAINER_NAME="${APP_NAME}"
|
||||
PORT="8002"
|
||||
USE_SUDO=false
|
||||
|
||||
# Docker命令包装函数
|
||||
docker_cmd() {
|
||||
if [ "$USE_SUDO" = true ]; then
|
||||
sudo docker "$@"
|
||||
else
|
||||
docker "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Docker Compose命令包装函数
|
||||
docker_compose_cmd() {
|
||||
if [ "$USE_SUDO" = true ]; then
|
||||
sudo docker-compose "$@"
|
||||
else
|
||||
docker-compose "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Docker 是否安装
|
||||
check_docker() {
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker 未安装,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Docker权限
|
||||
if ! docker info &> /dev/null; then
|
||||
log_warning "Docker权限不足,尝试修复..."
|
||||
|
||||
# 检查当前用户是否在docker组中
|
||||
if ! groups $(whoami) | grep -q docker; then
|
||||
log_warning "当前用户不在docker组中"
|
||||
|
||||
# 尝试将用户添加到docker组
|
||||
if command -v sudo &> /dev/null; then
|
||||
log_info "尝试将用户添加到docker组(需要sudo权限)..."
|
||||
sudo usermod -aG docker $(whoami) 2>/dev/null || {
|
||||
log_warning "无法自动添加用户到docker组"
|
||||
log_info "请手动执行: sudo usermod -aG docker $(whoami)"
|
||||
log_info "然后重新登录或执行: newgrp docker"
|
||||
}
|
||||
else
|
||||
log_error "sudo命令不可用,无法修复Docker权限"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 尝试使用sudo运行docker
|
||||
if command -v sudo &> /dev/null && sudo docker info &> /dev/null; then
|
||||
log_info "检测到可以使用sudo运行Docker"
|
||||
USE_SUDO=true
|
||||
else
|
||||
log_error "Docker权限不足且无法修复"
|
||||
log_info "解决方案:"
|
||||
log_info "1. 将用户添加到docker组: sudo usermod -aG docker $(whoami)"
|
||||
log_info "2. 重新登录或执行: newgrp docker"
|
||||
log_info "3. 或使用sudo运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Docker 已安装并可访问"
|
||||
}
|
||||
|
||||
# 检查 Docker Compose 是否安装
|
||||
check_docker_compose() {
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
log_error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Docker Compose 已安装"
|
||||
}
|
||||
|
||||
# 停止并删除现有容器
|
||||
stop_existing_container() {
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_warning "停止现有容器 ${CONTAINER_NAME}"
|
||||
docker_cmd stop ${CONTAINER_NAME}
|
||||
fi
|
||||
|
||||
if docker_cmd ps -aq -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_warning "删除现有容器 ${CONTAINER_NAME}"
|
||||
docker_cmd rm ${CONTAINER_NAME}
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_image() {
|
||||
log_info "开始构建 Docker 镜像..."
|
||||
docker_cmd build -t ${IMAGE_NAME} .
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "镜像构建成功: ${IMAGE_NAME}"
|
||||
else
|
||||
log_error "镜像构建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 运行容器
|
||||
run_container() {
|
||||
log_info "启动容器..."
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p uploads logs
|
||||
|
||||
docker_cmd run -d \
|
||||
--name ${CONTAINER_NAME} \
|
||||
--restart unless-stopped \
|
||||
-p ${PORT}:${PORT} \
|
||||
-v $(pwd)/uploads:/app/uploads \
|
||||
-v $(pwd)/logs:/app/logs \
|
||||
-e ENVIRONMENT=production \
|
||||
-e TZ=Asia/Shanghai \
|
||||
${IMAGE_NAME}
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "容器启动成功: ${CONTAINER_NAME}"
|
||||
else
|
||||
log_error "容器启动失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查容器状态
|
||||
check_container() {
|
||||
log_info "检查容器状态..."
|
||||
sleep 5
|
||||
|
||||
if docker_cmd ps | grep -q ${CONTAINER_NAME}; then
|
||||
log_success "容器运行正常"
|
||||
|
||||
# 显示容器日志
|
||||
log_info "容器日志:"
|
||||
docker_cmd logs ${CONTAINER_NAME}
|
||||
|
||||
# 测试健康检查
|
||||
log_info "测试健康检查..."
|
||||
sleep 10
|
||||
if curl -f http://localhost:${PORT}/api/v1/health &> /dev/null; then
|
||||
log_success "健康检查通过!"
|
||||
else
|
||||
log_warning "健康检查失败,但容器仍在运行"
|
||||
fi
|
||||
else
|
||||
log_error "容器未正常运行"
|
||||
log_error "容器日志:"
|
||||
docker_cmd logs ${CONTAINER_NAME}
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 使用 Docker Compose 部署
|
||||
deploy_with_compose() {
|
||||
log_info "使用 Docker Compose 部署..."
|
||||
|
||||
# 创建 .env 文件(如果不存在)
|
||||
if [ ! -f .env ]; then
|
||||
log_warning "创建 .env 文件,请根据实际情况修改配置"
|
||||
cat > .env << EOF
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql://username:password@mysql:3306/mytest_db
|
||||
MYSQL_ROOT_PASSWORD=rootpassword
|
||||
MYSQL_DATABASE=mytest_db
|
||||
MYSQL_USER=username
|
||||
MYSQL_PASSWORD=password
|
||||
|
||||
# Redis 配置
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
|
||||
# 应用配置
|
||||
SECRET_KEY=your-production-secret-key
|
||||
CORS_ORIGINS=http://localhost:3003,https://yourdomain.com
|
||||
ENVIRONMENT=production
|
||||
EOF
|
||||
log_warning "请编辑 .env 文件设置正确的配置"
|
||||
fi
|
||||
|
||||
docker_compose_cmd down
|
||||
docker_compose_cmd up -d --build
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "Docker Compose 部署成功"
|
||||
log_info "等待服务启动..."
|
||||
sleep 15
|
||||
|
||||
if curl -f http://localhost:${PORT}/api/v1/health &> /dev/null; then
|
||||
log_success "应用启动成功!"
|
||||
log_info "服务地址: http://localhost:${PORT}"
|
||||
log_info "API文档: http://localhost:${PORT}/docs"
|
||||
log_info "健康检查: http://localhost:${PORT}/api/v1/health"
|
||||
else
|
||||
log_warning "应用启动可能有问题,请检查日志"
|
||||
log_info "查看日志命令: docker-compose logs -f"
|
||||
docker_compose_cmd logs --tail=20
|
||||
fi
|
||||
else
|
||||
log_error "Docker Compose 部署失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理镜像和容器
|
||||
cleanup() {
|
||||
log_info "清理旧的镜像和容器..."
|
||||
|
||||
# 停止并删除容器
|
||||
stop_existing_container
|
||||
|
||||
# 删除镜像
|
||||
if docker_cmd images -q ${IMAGE_NAME} | grep -q .; then
|
||||
log_warning "删除旧镜像: ${IMAGE_NAME}"
|
||||
docker_cmd rmi ${IMAGE_NAME} 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 清理未使用的镜像和容器
|
||||
log_info "清理未使用的 Docker 资源..."
|
||||
docker_cmd system prune -f
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "云盘应用 Docker 部署脚本 (权限修复版)"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " build - 仅构建镜像"
|
||||
echo " run - 仅运行容器(需要先构建镜像)"
|
||||
echo " compose - 使用 Docker Compose 部署"
|
||||
echo " stop - 停止容器"
|
||||
echo " restart - 重启容器"
|
||||
echo " logs - 查看容器日志"
|
||||
echo " cleanup - 清理镜像和容器"
|
||||
echo " status - 查看容器状态"
|
||||
echo " help - 显示此帮助信息"
|
||||
echo ""
|
||||
echo "默认行为: 构建镜像并运行容器"
|
||||
echo ""
|
||||
echo "权限修复功能:"
|
||||
echo "- 自动检测Docker权限"
|
||||
echo "- 尝试自动修复权限问题"
|
||||
echo "- 支持sudo模式运行Docker"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "${1:-}" in
|
||||
"build")
|
||||
check_docker
|
||||
build_image
|
||||
;;
|
||||
"run")
|
||||
check_docker
|
||||
stop_existing_container
|
||||
run_container
|
||||
check_container
|
||||
;;
|
||||
"compose")
|
||||
check_docker
|
||||
check_docker_compose
|
||||
deploy_with_compose
|
||||
;;
|
||||
"stop")
|
||||
check_docker
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker_cmd stop ${CONTAINER_NAME}
|
||||
log_success "容器已停止"
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"restart")
|
||||
check_docker
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker_cmd restart ${CONTAINER_NAME}
|
||||
log_success "容器已重启"
|
||||
sleep 5
|
||||
check_container
|
||||
else
|
||||
log_warning "容器未运行,尝试启动..."
|
||||
stop_existing_container
|
||||
build_image
|
||||
run_container
|
||||
check_container
|
||||
fi
|
||||
;;
|
||||
"logs")
|
||||
check_docker
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker_cmd logs -f ${CONTAINER_NAME}
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"cleanup")
|
||||
check_docker
|
||||
cleanup
|
||||
;;
|
||||
"status")
|
||||
check_docker
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_success "容器正在运行"
|
||||
docker_cmd ps | grep ${CONTAINER_NAME}
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
;;
|
||||
"")
|
||||
log_info "开始构建和部署云盘应用..."
|
||||
check_docker
|
||||
stop_existing_container
|
||||
build_image
|
||||
run_container
|
||||
check_container
|
||||
log_success "部署完成!应用正在运行中"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
@@ -1,360 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 云盘应用 Docker 构建和部署脚本
|
||||
# 用于构建生产环境的 Docker 镜像并部署到 Linux 环境
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="cloud-drive-backend"
|
||||
IMAGE_NAME="${APP_NAME}:latest"
|
||||
CONTAINER_NAME="${APP_NAME}"
|
||||
PORT="8002"
|
||||
USE_SUDO=false
|
||||
|
||||
# Docker命令包装函数
|
||||
docker_cmd() {
|
||||
if [ "$USE_SUDO" = true ]; then
|
||||
sudo docker "$@"
|
||||
else
|
||||
docker "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Docker Compose命令包装函数
|
||||
docker_compose_cmd() {
|
||||
if [ "$USE_SUDO" = true ]; then
|
||||
sudo docker-compose "$@"
|
||||
else
|
||||
docker-compose "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Docker 是否安装
|
||||
check_docker() {
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker 未安装,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Docker权限
|
||||
if ! docker info &> /dev/null; then
|
||||
log_warning "Docker权限不足,尝试修复..."
|
||||
|
||||
# 检查当前用户是否在docker组中
|
||||
if ! groups $(whoami) | grep -q docker; then
|
||||
log_warning "当前用户不在docker组中"
|
||||
|
||||
# 尝试将用户添加到docker组
|
||||
if command -v sudo &> /dev/null; then
|
||||
log_info "尝试将用户添加到docker组(需要sudo权限)..."
|
||||
sudo usermod -aG docker $(whoami) 2>/dev/null || {
|
||||
log_warning "无法自动添加用户到docker组"
|
||||
log_info "请手动执行: sudo usermod -aG docker $(whoami)"
|
||||
log_info "然后重新登录或执行: newgrp docker"
|
||||
}
|
||||
else
|
||||
log_error "sudo命令不可用,无法修复Docker权限"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 尝试使用sudo运行docker
|
||||
if command -v sudo &> /dev/null && sudo docker info &> /dev/null; then
|
||||
log_info "检测到可以使用sudo运行Docker"
|
||||
USE_SUDO=true
|
||||
else
|
||||
log_error "Docker权限不足且无法修复"
|
||||
log_info "解决方案:"
|
||||
log_info "1. 将用户添加到docker组: sudo usermod -aG docker $(whoami)"
|
||||
log_info "2. 重新登录或执行: newgrp docker"
|
||||
log_info "3. 或使用sudo运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "Docker 已安装并可访问"
|
||||
}
|
||||
|
||||
# 检查 Docker Compose 是否安装
|
||||
check_docker_compose() {
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
log_error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
log_success "Docker Compose 已安装"
|
||||
}
|
||||
|
||||
# 停止并删除现有容器
|
||||
stop_existing_container() {
|
||||
if docker_cmd ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_warning "停止现有容器 ${CONTAINER_NAME}"
|
||||
docker_cmd stop ${CONTAINER_NAME}
|
||||
fi
|
||||
|
||||
if docker_cmd ps -aq -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_warning "删除现有容器 ${CONTAINER_NAME}"
|
||||
docker_cmd rm ${CONTAINER_NAME}
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_image() {
|
||||
log_info "开始构建 Docker 镜像..."
|
||||
docker_cmd build -t ${IMAGE_NAME} .
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "镜像构建成功: ${IMAGE_NAME}"
|
||||
else
|
||||
log_error "镜像构建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 运行容器
|
||||
run_container() {
|
||||
log_info "启动容器..."
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p uploads logs
|
||||
|
||||
docker_cmd run -d \
|
||||
--name ${CONTAINER_NAME} \
|
||||
--restart unless-stopped \
|
||||
-p ${PORT}:${PORT} \
|
||||
-v $(pwd)/uploads:/app/uploads \
|
||||
-v $(pwd)/logs:/app/logs \
|
||||
-e ENVIRONMENT=production \
|
||||
-e TZ=Asia/Shanghai \
|
||||
${IMAGE_NAME}
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "容器启动成功: ${CONTAINER_NAME}"
|
||||
else
|
||||
log_error "容器启动失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查容器状态
|
||||
check_container() {
|
||||
log_info "检查容器状态..."
|
||||
sleep 5
|
||||
|
||||
if docker_cmd ps | grep -q ${CONTAINER_NAME}; then
|
||||
log_success "容器运行正常"
|
||||
|
||||
# 显示容器日志
|
||||
log_info "容器日志:"
|
||||
docker_cmd logs ${CONTAINER_NAME}
|
||||
|
||||
# 测试健康检查
|
||||
log_info "测试健康检查..."
|
||||
sleep 10
|
||||
if curl -f http://localhost:${PORT}/api/v1/health &> /dev/null; then
|
||||
log_success "健康检查通过!"
|
||||
else
|
||||
log_warning "健康检查失败,但容器仍在运行"
|
||||
fi
|
||||
else
|
||||
log_error "容器未正常运行"
|
||||
log_error "容器日志:"
|
||||
docker_cmd logs ${CONTAINER_NAME}
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 使用 Docker Compose 部署
|
||||
deploy_with_compose() {
|
||||
log_info "使用 Docker Compose 部署..."
|
||||
|
||||
# 创建 .env 文件(如果不存在)
|
||||
if [ ! -f .env ]; then
|
||||
log_warning "创建 .env 文件,请根据实际情况修改配置"
|
||||
cat > .env << EOF
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql://username:password@mysql:3306/mytest_db
|
||||
MYSQL_ROOT_PASSWORD=rootpassword
|
||||
MYSQL_DATABASE=mytest_db
|
||||
MYSQL_USER=username
|
||||
MYSQL_PASSWORD=password
|
||||
|
||||
# Redis 配置
|
||||
REDIS_URL=redis://redis:6379/0
|
||||
|
||||
# 应用配置
|
||||
SECRET_KEY=your-production-secret-key
|
||||
CORS_ORIGINS=http://localhost:3003,https://yourdomain.com
|
||||
ENVIRONMENT=production
|
||||
EOF
|
||||
log_warning "请编辑 .env 文件设置正确的配置"
|
||||
fi
|
||||
|
||||
docker_compose_cmd down
|
||||
docker_compose_cmd up -d --build
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "Docker Compose 部署成功"
|
||||
log_info "等待服务启动..."
|
||||
sleep 15
|
||||
|
||||
if curl -f http://localhost:${PORT}/api/v1/health &> /dev/null; then
|
||||
log_success "应用启动成功!"
|
||||
log_info "服务地址: http://localhost:${PORT}"
|
||||
log_info "API文档: http://localhost:${PORT}/docs"
|
||||
log_info "健康检查: http://localhost:${PORT}/api/v1/health"
|
||||
else
|
||||
log_warning "应用启动可能有问题,请检查日志"
|
||||
log_info "查看日志命令: docker-compose logs -f"
|
||||
docker_compose_cmd logs --tail=20
|
||||
fi
|
||||
else
|
||||
log_error "Docker Compose 部署失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理镜像和容器
|
||||
cleanup() {
|
||||
log_info "清理旧的镜像和容器..."
|
||||
|
||||
# 停止并删除容器
|
||||
stop_existing_container
|
||||
|
||||
# 删除镜像
|
||||
if docker_cmd images -q ${IMAGE_NAME} | grep -q .; then
|
||||
log_warning "删除旧镜像: ${IMAGE_NAME}"
|
||||
docker_cmd rmi ${IMAGE_NAME} 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 清理未使用的镜像和容器
|
||||
log_info "清理未使用的 Docker 资源..."
|
||||
docker_cmd system prune -f
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "云盘应用 Docker 部署脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " build - 仅构建镜像"
|
||||
echo " run - 仅运行容器(需要先构建镜像)"
|
||||
echo " compose - 使用 Docker Compose 部署"
|
||||
echo " stop - 停止容器"
|
||||
echo " restart - 重启容器"
|
||||
echo " logs - 查看容器日志"
|
||||
echo " cleanup - 清理镜像和容器"
|
||||
echo " status - 查看容器状态"
|
||||
echo " help - 显示此帮助信息"
|
||||
echo ""
|
||||
echo "默认行为: 构建镜像并运行容器"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "${1:-}" in
|
||||
"build")
|
||||
check_docker
|
||||
build_image
|
||||
;;
|
||||
"run")
|
||||
check_docker
|
||||
stop_existing_container
|
||||
run_container
|
||||
check_container
|
||||
;;
|
||||
"compose")
|
||||
check_docker
|
||||
check_docker_compose
|
||||
deploy_with_compose
|
||||
;;
|
||||
"stop")
|
||||
if docker ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker stop ${CONTAINER_NAME}
|
||||
log_success "容器已停止"
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"restart")
|
||||
if docker ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker restart ${CONTAINER_NAME}
|
||||
log_success "容器已重启"
|
||||
sleep 5
|
||||
check_container
|
||||
else
|
||||
log_warning "容器未运行,尝试启动..."
|
||||
check_docker
|
||||
stop_existing_container
|
||||
run_container
|
||||
check_container
|
||||
fi
|
||||
;;
|
||||
"logs")
|
||||
if docker ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
docker logs -f ${CONTAINER_NAME}
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"cleanup")
|
||||
check_docker
|
||||
cleanup
|
||||
;;
|
||||
"status")
|
||||
if docker ps -q -f name=${CONTAINER_NAME} | grep -q .; then
|
||||
log_success "容器正在运行"
|
||||
docker ps | grep ${CONTAINER_NAME}
|
||||
else
|
||||
log_warning "容器未运行"
|
||||
fi
|
||||
;;
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
;;
|
||||
"")
|
||||
log_info "开始构建和部署云盘应用..."
|
||||
check_docker
|
||||
stop_existing_container
|
||||
build_image
|
||||
run_container
|
||||
check_container
|
||||
log_success "部署完成!应用正在运行中"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
186
backend/build.py
186
backend/build.py
@@ -1,186 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
云盘应用打包脚本
|
||||
用于将FastAPI应用打包为可执行文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
def clean_build():
|
||||
"""清理之前的构建文件"""
|
||||
print("清理之前的构建文件...")
|
||||
|
||||
# 清理PyInstaller生成的文件
|
||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
||||
for dir_name in dirs_to_clean:
|
||||
if os.path.exists(dir_name):
|
||||
shutil.rmtree(dir_name)
|
||||
print(f" ✅ 已删除: {dir_name}")
|
||||
|
||||
# 清理.spec文件
|
||||
if os.path.exists('main.spec'):
|
||||
os.remove('main.spec')
|
||||
print(f" ✅ 已删除: main.spec")
|
||||
|
||||
def check_dependencies():
|
||||
"""检查依赖是否安装"""
|
||||
print("📦 检查依赖...")
|
||||
|
||||
required_packages = ['fastapi', 'uvicorn', 'pydantic', 'sqlalchemy', 'loguru']
|
||||
missing_packages = []
|
||||
|
||||
for package in required_packages:
|
||||
try:
|
||||
__import__(package)
|
||||
print(f" ✅ {package}")
|
||||
except ImportError:
|
||||
missing_packages.append(package)
|
||||
print(f" ❌ {package} (缺失)")
|
||||
|
||||
if missing_packages:
|
||||
print(f"\n❌ 缺少以下依赖: {', '.join(missing_packages)}")
|
||||
print("请运行: pip install -r requirements.txt")
|
||||
return False
|
||||
|
||||
print("✅ 所有依赖都已安装")
|
||||
return True
|
||||
|
||||
def build_executable():
|
||||
"""构建可执行文件"""
|
||||
print("🔨 开始构建可执行文件...")
|
||||
|
||||
# 使用自定义的spec文件进行构建
|
||||
import subprocess
|
||||
result = subprocess.run([
|
||||
sys.executable, '-m', 'PyInstaller',
|
||||
'build.spec',
|
||||
'--clean',
|
||||
'--noconfirm'
|
||||
], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✅ 构建成功!")
|
||||
print(f"📁 可执行文件位置: {os.path.abspath('dist/cloud-drive-server.exe')}")
|
||||
return True
|
||||
else:
|
||||
print("❌ 构建失败!")
|
||||
print("错误信息:")
|
||||
print(result.stderr)
|
||||
return False
|
||||
|
||||
def create_deployment_package():
|
||||
"""创建部署包"""
|
||||
print("📦 创建部署包...")
|
||||
|
||||
dist_dir = Path('dist')
|
||||
deploy_dir = Path('deploy')
|
||||
|
||||
# 创建部署目录
|
||||
if deploy_dir.exists():
|
||||
shutil.rmtree(deploy_dir)
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 复制可执行文件
|
||||
exe_path = dist_dir / 'cloud-drive-server.exe'
|
||||
if exe_path.exists():
|
||||
shutil.copy2(exe_path, deploy_dir / 'cloud-drive-server.exe')
|
||||
print(" ✅ 复制可执行文件")
|
||||
|
||||
# 复制配置文件
|
||||
config_files = ['requirements.txt', '.env.example']
|
||||
for config_file in config_files:
|
||||
if os.path.exists(config_file):
|
||||
shutil.copy2(config_file, deploy_dir / config_file)
|
||||
print(f" ✅ 复制配置文件: {config_file}")
|
||||
|
||||
# 创建启动脚本
|
||||
start_script = deploy_dir / 'start.bat'
|
||||
with open(start_script, 'w', encoding='utf-8') as f:
|
||||
f.write("""@echo off
|
||||
echo 🚀 启动云盘服务器...
|
||||
echo 📝 确保MySQL和Redis服务已启动
|
||||
echo.
|
||||
cloud-drive-server.exe
|
||||
pause
|
||||
""")
|
||||
print(" ✅ 创建启动脚本: start.bat")
|
||||
|
||||
# 创建README
|
||||
readme_path = deploy_dir / 'README.md'
|
||||
with open(readme_path, 'w', encoding='utf-8') as f:
|
||||
f.write("""# 云盘应用部署包
|
||||
|
||||
## 快速启动
|
||||
|
||||
1. **确保数据库和缓存服务运行**
|
||||
- MySQL服务器已启动
|
||||
- Redis服务器已启动(可选)
|
||||
|
||||
2. **配置环境变量**
|
||||
- 复制 `.env.example` 为 `.env`
|
||||
- 修改 `.env` 中的数据库连接信息
|
||||
|
||||
3. **启动应用**
|
||||
- Windows: 双击 `start.bat` 或运行 `cloud-drive-server.exe`
|
||||
- 访问 http://localhost:8000
|
||||
|
||||
## 配置说明
|
||||
|
||||
在 `.env` 文件中配置以下参数:
|
||||
|
||||
```env
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql+pymysql://username:password@localhost:3306/database_name
|
||||
|
||||
# JWT密钥
|
||||
SECRET_KEY=your-secret-key-here
|
||||
|
||||
# 其他配置...
|
||||
```
|
||||
|
||||
## API文档
|
||||
|
||||
启动后访问:
|
||||
- Swagger UI: http://localhost:8000/docs
|
||||
- ReDoc: http://localhost:8000/redoc
|
||||
|
||||
## 故障排除
|
||||
|
||||
1. **端口被占用**: 修改 `.env` 中的 `PORT` 配置
|
||||
2. **数据库连接失败**: 检查MySQL服务状态和连接配置
|
||||
3. **缺少依赖**: 确保所有依赖已正确安装
|
||||
""")
|
||||
print(" ✅ 创建README文档")
|
||||
|
||||
print(f"📁 部署包位置: {deploy_dir.absolute()}")
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🏗️ 云盘应用打包工具")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. 检查依赖
|
||||
if not check_dependencies():
|
||||
sys.exit(1)
|
||||
|
||||
# 2. 清理之前的构建
|
||||
clean_build()
|
||||
|
||||
# 3. 构建可执行文件
|
||||
if not build_executable():
|
||||
sys.exit(1)
|
||||
|
||||
# 4. 创建部署包
|
||||
if not create_deployment_package():
|
||||
sys.exit(1)
|
||||
|
||||
print("\n🎉 打包完成!")
|
||||
print("📁 部署包位于 'deploy' 目录")
|
||||
print("🚀 可以将整个 deploy 文件夹复制到目标服务器运行")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
echo 正在为Linux打包Python程序...
|
||||
docker run --rm -v "%cd%:/src" cdrx/pyinstaller-linux:python3-20231002 "pyinstaller --onefile main.py"
|
||||
echo 打包完成!检查 dist/ 目录
|
||||
@@ -1,332 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
云盘后端Linux打包脚本
|
||||
使用PyInstaller将后端应用打包成Linux可执行文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
|
||||
def check_python_version():
|
||||
"""检查Python版本"""
|
||||
if sys.version_info < (3, 8):
|
||||
print("错误: 需要Python 3.8或更高版本")
|
||||
sys.exit(1)
|
||||
print(f"[OK] Python版本: {sys.version}")
|
||||
|
||||
def check_dependencies():
|
||||
"""检查必要的依赖"""
|
||||
try:
|
||||
import PyInstaller
|
||||
print(f"[OK] PyInstaller版本: {PyInstaller.__version__}")
|
||||
except ImportError:
|
||||
print("错误: 未安装PyInstaller")
|
||||
print("请运行: pip install pyinstaller")
|
||||
sys.exit(1)
|
||||
|
||||
def clean_build_dirs():
|
||||
"""清理之前的构建目录"""
|
||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
||||
for dir_name in dirs_to_clean:
|
||||
if os.path.exists(dir_name):
|
||||
print(f"清理目录: {dir_name}")
|
||||
shutil.rmtree(dir_name)
|
||||
|
||||
# 清理Python缓存文件
|
||||
for root, dirs, files in os.walk('.'):
|
||||
for file in files:
|
||||
if file.endswith('.pyc') or file.endswith('.pyo'):
|
||||
os.remove(os.path.join(root, file))
|
||||
if '__pycache__' in dirs:
|
||||
shutil.rmtree(os.path.join(root, '__pycache__'))
|
||||
|
||||
def create_spec_file():
|
||||
"""创建或更新PyInstaller规格文件"""
|
||||
# 直接使用现有的build.spec文件
|
||||
print("[OK] build.spec 文件已存在,跳过创建")
|
||||
|
||||
def run_pyinstaller():
|
||||
"""运行PyInstaller进行打包"""
|
||||
print("开始打包...")
|
||||
try:
|
||||
# 使用spec文件进行打包
|
||||
cmd = ['pyinstaller', '--clean', 'build.spec']
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
print("[OK] PyInstaller执行成功")
|
||||
if result.stdout:
|
||||
print("输出:", result.stdout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"错误: PyInstaller执行失败: {e}")
|
||||
if e.stderr:
|
||||
print("错误输出:", e.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def create_deployment_package():
|
||||
"""创建部署包"""
|
||||
dist_dir = Path('dist')
|
||||
deploy_dir = Path('deploy')
|
||||
|
||||
if deploy_dir.exists():
|
||||
shutil.rmtree(deploy_dir)
|
||||
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 复制可执行文件
|
||||
exe_file = dist_dir / 'cloud-drive-server'
|
||||
if exe_file.exists():
|
||||
shutil.copy2(exe_file, deploy_dir)
|
||||
print(f"[OK] 复制可执行文件到 {deploy_dir}")
|
||||
|
||||
# 复制配置文件
|
||||
config_files = ['.env.example']
|
||||
for config_file in config_files:
|
||||
if os.path.exists(config_file):
|
||||
shutil.copy2(config_file, deploy_dir)
|
||||
print(f"[OK] 复制配置文件 {config_file}")
|
||||
|
||||
# 复制安装脚本
|
||||
install_scripts = ['install.sh', 'install_user.sh']
|
||||
for script in install_scripts:
|
||||
if os.path.exists(script):
|
||||
shutil.copy2(script, deploy_dir)
|
||||
os.chmod(deploy_dir / script, 0o755)
|
||||
print(f"[OK] 复制安装脚本 {script}")
|
||||
|
||||
# 创建部署目录结构
|
||||
(deploy_dir / 'logs').mkdir(exist_ok=True)
|
||||
(deploy_dir / 'uploads').mkdir(exist_ok=True)
|
||||
|
||||
# 创建启动脚本
|
||||
create_startup_script(deploy_dir)
|
||||
|
||||
# 创建README
|
||||
create_readme(deploy_dir)
|
||||
|
||||
print(f"[OK] 部署包创建完成: {deploy_dir.absolute()}")
|
||||
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 复制可执行文件
|
||||
exe_file = dist_dir / 'cloud-drive-server'
|
||||
if exe_file.exists():
|
||||
shutil.copy2(exe_file, deploy_dir)
|
||||
print(f"[OK] 复制可执行文件到 {deploy_dir}")
|
||||
|
||||
# 复制配置文件
|
||||
config_files = ['.env.example']
|
||||
for config_file in config_files:
|
||||
if os.path.exists(config_file):
|
||||
shutil.copy2(config_file, deploy_dir)
|
||||
print(f"[OK] 复制配置文件 {config_file}")
|
||||
|
||||
# 创建部署目录结构
|
||||
(deploy_dir / 'logs').mkdir(exist_ok=True)
|
||||
(deploy_dir / 'uploads').mkdir(exist_ok=True)
|
||||
|
||||
# 创建启动脚本
|
||||
create_startup_script(deploy_dir)
|
||||
|
||||
# 创建README
|
||||
create_readme(deploy_dir)
|
||||
|
||||
print(f"[OK] 部署包创建完成: {deploy_dir.absolute()}")
|
||||
|
||||
def create_startup_script(deploy_dir):
|
||||
"""创建启动脚本"""
|
||||
# Linux启动脚本
|
||||
startup_script = '''#!/bin/bash
|
||||
# 云盘后端服务启动脚本
|
||||
|
||||
# 设置环境变量
|
||||
export PYTHONPATH=${PYTHONPATH}:$(dirname "$0")
|
||||
|
||||
# 进入脚本所在目录
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "警告: .env 文件不存在,将使用默认配置"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example .env
|
||||
echo "已复制 .env.example 为 .env,请根据需要修改配置"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p logs uploads
|
||||
|
||||
# 启动服务
|
||||
echo "启动云盘后端服务..."
|
||||
./cloud-drive-server
|
||||
'''
|
||||
|
||||
script_path = deploy_dir / 'start.sh'
|
||||
with open(script_path, 'w', encoding='utf-8') as f:
|
||||
f.write(startup_script)
|
||||
|
||||
# 设置执行权限
|
||||
os.chmod(script_path, 0o755)
|
||||
print("[OK] 创建启动脚本 start.sh")
|
||||
|
||||
def create_readme(deploy_dir):
|
||||
"""创建部署说明文档"""
|
||||
readme_content = '''# 云盘后端服务部署说明
|
||||
|
||||
## 文件说明
|
||||
|
||||
- `cloud-drive-server`: 主程序可执行文件
|
||||
- `start.sh`: 启动脚本
|
||||
- `.env.example`: 环境配置示例文件
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. **配置环境变量**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# 编辑 .env 文件,配置数据库和Redis连接信息
|
||||
nano .env
|
||||
```
|
||||
|
||||
2. **启动服务**
|
||||
```bash
|
||||
chmod +x start.sh
|
||||
./start.sh
|
||||
```
|
||||
|
||||
或者直接运行:
|
||||
```bash
|
||||
./cloud-drive-server
|
||||
```
|
||||
|
||||
3. **访问服务**
|
||||
- API文档: http://localhost:8000/docs
|
||||
- 健康检查: http://localhost:8000/api/v1/health
|
||||
|
||||
## 环境配置
|
||||
|
||||
主要配置项(.env文件):
|
||||
|
||||
```env
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://主机:端口
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY=你的密钥
|
||||
JWT_EXPIRE_MINUTES=30
|
||||
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=uploads
|
||||
MAX_FILE_SIZE=10485760 # 10MB
|
||||
```
|
||||
|
||||
## 系统要求
|
||||
|
||||
- Linux 64位系统
|
||||
- MySQL 5.7+ 或 8.0+
|
||||
- Redis (可选)
|
||||
- 至少512MB内存
|
||||
- 至少100MB磁盘空间
|
||||
|
||||
## 日志
|
||||
|
||||
日志文件位置:`logs/app.log`
|
||||
|
||||
## 问题排查
|
||||
|
||||
1. **端口占用**
|
||||
- 默认端口8000,如需修改请编辑.env文件
|
||||
|
||||
2. **数据库连接失败**
|
||||
- 检查DATABASE_URL配置
|
||||
- 确保数据库服务正在运行
|
||||
- 检查防火墙设置
|
||||
|
||||
3. **权限问题**
|
||||
- 确保程序有执行权限:`chmod +x cloud-drive-server`
|
||||
- 确保有写入logs和uploads目录的权限
|
||||
|
||||
## 后台运行
|
||||
|
||||
使用systemd或supervisor管理服务进程:
|
||||
|
||||
### systemd 配置示例
|
||||
|
||||
创建服务文件 `/etc/systemd/system/cloud-drive.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Cloud Drive Backend Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/path/to/deploy/directory
|
||||
ExecStart=/path/to/deploy/directory/cloud-drive-server
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
启用和启动服务:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable cloud-drive
|
||||
sudo systemctl start cloud-drive
|
||||
```
|
||||
'''
|
||||
|
||||
readme_path = deploy_dir / 'README.md'
|
||||
with open(readme_path, 'w', encoding='utf-8') as f:
|
||||
f.write(readme_content)
|
||||
print("[OK] 创建部署说明文档 README.md")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
parser = argparse.ArgumentParser(description='云盘后端Linux打包工具')
|
||||
parser.add_argument('--clean', action='store_true', help='仅清理构建目录')
|
||||
parser.add_argument('--no-clean', action='store_true', help='跳过清理步骤')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== 云盘后端Linux打包工具 ===")
|
||||
print(f"当前目录: {os.getcwd()}")
|
||||
|
||||
# 检查环境
|
||||
check_python_version()
|
||||
check_dependencies()
|
||||
|
||||
# 清理构建目录
|
||||
if args.clean:
|
||||
clean_build_dirs()
|
||||
print("[OK] 清理完成")
|
||||
return
|
||||
|
||||
if not args.no_clean:
|
||||
clean_build_dirs()
|
||||
|
||||
# 创建规格文件
|
||||
create_spec_file()
|
||||
|
||||
# 运行打包
|
||||
run_pyinstaller()
|
||||
|
||||
# 创建部署包
|
||||
create_deployment_package()
|
||||
|
||||
print("\n=== 打包完成 ===")
|
||||
print("部署包位置: ./deploy/")
|
||||
print("请查看 ./deploy/README.md 了解部署说明")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,312 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
云盘后端Linux打包脚本
|
||||
使用PyInstaller将后端应用打包成Linux可执行文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
|
||||
def check_python_version():
|
||||
"""检查Python版本"""
|
||||
if sys.version_info < (3, 8):
|
||||
print("错误: 需要Python 3.8或更高版本")
|
||||
sys.exit(1)
|
||||
print(f"[OK] Python版本: {sys.version}")
|
||||
|
||||
def check_dependencies():
|
||||
"""检查必要的依赖"""
|
||||
try:
|
||||
import PyInstaller
|
||||
print(f"[OK] PyInstaller版本: {PyInstaller.__version__}")
|
||||
except ImportError:
|
||||
print("错误: 未安装PyInstaller")
|
||||
print("请运行: pip install pyinstaller")
|
||||
sys.exit(1)
|
||||
|
||||
def clean_build_dirs():
|
||||
"""清理之前的构建目录"""
|
||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
||||
for dir_name in dirs_to_clean:
|
||||
if os.path.exists(dir_name):
|
||||
print(f"清理目录: {dir_name}")
|
||||
shutil.rmtree(dir_name)
|
||||
|
||||
# 清理Python缓存文件
|
||||
for root, dirs, files in os.walk('.'):
|
||||
for file in files:
|
||||
if file.endswith('.pyc') or file.endswith('.pyo'):
|
||||
os.remove(os.path.join(root, file))
|
||||
if '__pycache__' in dirs:
|
||||
shutil.rmtree(os.path.join(root, '__pycache__'))
|
||||
|
||||
def create_spec_file():
|
||||
"""创建或更新PyInstaller规格文件"""
|
||||
# 直接使用现有的build.spec文件
|
||||
print("[OK] build.spec 文件已存在,跳过创建")
|
||||
|
||||
def run_pyinstaller():
|
||||
"""运行PyInstaller进行打包"""
|
||||
print("开始打包...")
|
||||
try:
|
||||
# 使用spec文件进行打包
|
||||
cmd = ['pyinstaller', '--clean', 'build.spec']
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
print("[OK] PyInstaller执行成功")
|
||||
if result.stdout:
|
||||
print("输出:", result.stdout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"错误: PyInstaller执行失败: {e}")
|
||||
if e.stderr:
|
||||
print("错误输出:", e.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def create_deployment_package():
|
||||
"""创建部署包"""
|
||||
dist_dir = Path('dist')
|
||||
deploy_dir = Path('deploy')
|
||||
|
||||
if deploy_dir.exists():
|
||||
shutil.rmtree(deploy_dir)
|
||||
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 复制可执行文件
|
||||
exe_file = dist_dir / 'cloud-drive-server'
|
||||
if exe_file.exists():
|
||||
shutil.copy2(exe_file, deploy_dir)
|
||||
print(f"[OK] 复制可执行文件到 {deploy_dir}")
|
||||
|
||||
# 复制配置文件
|
||||
config_files = ['.env.example']
|
||||
for config_file in config_files:
|
||||
if os.path.exists(config_file):
|
||||
shutil.copy2(config_file, deploy_dir)
|
||||
print(f"[OK] 复制配置文件 {config_file}")
|
||||
|
||||
# 复制安装脚本
|
||||
install_scripts = ['install.sh', 'install_user.sh']
|
||||
for script in install_scripts:
|
||||
if os.path.exists(script):
|
||||
shutil.copy2(script, deploy_dir)
|
||||
os.chmod(deploy_dir / script, 0o755)
|
||||
print(f"[OK] 复制安装脚本 {script}")
|
||||
|
||||
# 创建部署目录结构
|
||||
(deploy_dir / 'logs').mkdir(exist_ok=True)
|
||||
(deploy_dir / 'uploads').mkdir(exist_ok=True)
|
||||
|
||||
# 创建启动脚本
|
||||
create_startup_script(deploy_dir)
|
||||
|
||||
# 创建README
|
||||
create_readme(deploy_dir)
|
||||
|
||||
print(f"[OK] 部署包创建完成: {deploy_dir.absolute()}")
|
||||
|
||||
def create_startup_script(deploy_dir):
|
||||
"""创建启动脚本"""
|
||||
# Linux启动脚本
|
||||
startup_script = '''#!/bin/bash
|
||||
# 云盘后端服务启动脚本
|
||||
|
||||
# 设置环境变量
|
||||
export PYTHONPATH=${PYTHONPATH}:$(dirname "$0")
|
||||
|
||||
# 进入脚本所在目录
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "警告: .env 文件不存在,将使用默认配置"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example .env
|
||||
echo "已复制 .env.example 为 .env,请根据需要修改配置"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p logs uploads
|
||||
|
||||
# 启动服务
|
||||
echo "启动云盘后端服务..."
|
||||
./cloud-drive-server
|
||||
'''
|
||||
|
||||
script_path = deploy_dir / 'start.sh'
|
||||
with open(script_path, 'w', encoding='utf-8') as f:
|
||||
f.write(startup_script)
|
||||
|
||||
# 设置执行权限
|
||||
os.chmod(script_path, 0o755)
|
||||
print("[OK] 创建启动脚本 start.sh")
|
||||
|
||||
def create_readme(deploy_dir):
|
||||
"""创建部署说明文档"""
|
||||
readme_content = '''# 云盘后端服务部署说明
|
||||
|
||||
## 文件说明
|
||||
|
||||
- `cloud-drive-server`: 主程序可执行文件
|
||||
- `start.sh`: 启动脚本
|
||||
- `install.sh`: 系统级安装脚本(需要sudo权限)
|
||||
- `install_user.sh`: 用户级安装脚本(无需sudo权限)
|
||||
- `.env.example`: 环境配置示例文件
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 方法一:用户级安装(推荐,无需sudo权限)
|
||||
|
||||
```bash
|
||||
# 1. 运行用户级安装脚本
|
||||
./install_user.sh
|
||||
|
||||
# 2. 配置环境变量
|
||||
cd ~/cloud-drive
|
||||
cp .env.example .env
|
||||
nano .env # 编辑配置文件
|
||||
|
||||
# 3. 启动服务
|
||||
./start.sh
|
||||
|
||||
# 4. 查看状态
|
||||
./status.sh
|
||||
```
|
||||
|
||||
### 方法二:系统级安装(需要sudo权限)
|
||||
|
||||
```bash
|
||||
# 1. 运行系统级安装脚本
|
||||
sudo ./install.sh
|
||||
|
||||
# 2. 启动服务
|
||||
sudo systemctl start cloud-drive
|
||||
|
||||
# 3. 查看状态
|
||||
sudo systemctl status cloud-drive
|
||||
```
|
||||
|
||||
### 方法三:直接运行
|
||||
|
||||
```bash
|
||||
# 1. 配置环境变量
|
||||
cp .env.example .env
|
||||
nano .env # 编辑配置文件
|
||||
|
||||
# 2. 启动服务
|
||||
chmod +x cloud-drive-server
|
||||
./cloud-drive-server
|
||||
```
|
||||
|
||||
## 环境配置
|
||||
|
||||
主要配置项(.env文件):
|
||||
|
||||
```env
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://主机:端口
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY=你的密钥
|
||||
JWT_EXPIRE_MINUTES=30
|
||||
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=uploads
|
||||
MAX_FILE_SIZE=10485760 # 10MB
|
||||
```
|
||||
|
||||
## 访问服务
|
||||
|
||||
- API文档: http://localhost:8000/docs
|
||||
- 健康检查: http://localhost:8000/api/v1/health
|
||||
- 根路径: http://localhost:8000/
|
||||
|
||||
## 系统要求
|
||||
|
||||
- Linux 64位系统
|
||||
- MySQL 5.7+ 或 8.0+
|
||||
- Redis (可选)
|
||||
- 至少512MB内存
|
||||
- 至少100MB磁盘空间
|
||||
|
||||
## 问题排查
|
||||
|
||||
1. **端口占用**
|
||||
- 默认端口8000,如需修改请编辑.env文件
|
||||
|
||||
2. **数据库连接失败**
|
||||
- 检查DATABASE_URL配置
|
||||
- 确保数据库服务正在运行
|
||||
|
||||
3. **权限问题**
|
||||
- 确保程序有执行权限:`chmod +x cloud-drive-server`
|
||||
- 确保有写入logs和uploads目录的权限
|
||||
|
||||
4. **依赖缺失**
|
||||
- 如果出现模块缺失错误,请确保打包包含了所有依赖
|
||||
- 可以尝试重新运行打包脚本
|
||||
|
||||
## 日志
|
||||
|
||||
日志文件位置:
|
||||
- 用户级安装:`~/.local/share/cloud-drive/logs/app.log`
|
||||
- 系统级安装:`/opt/cloud-drive/logs/app.log`
|
||||
- 直接运行:`./logs/app.log`
|
||||
'''
|
||||
|
||||
readme_path = deploy_dir / 'README.md'
|
||||
with open(readme_path, 'w', encoding='utf-8') as f:
|
||||
f.write(readme_content)
|
||||
print("[OK] 创建部署说明文档 README.md")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
parser = argparse.ArgumentParser(description='云盘后端Linux打包工具')
|
||||
parser.add_argument('--clean', action='store_true', help='仅清理构建目录')
|
||||
parser.add_argument('--no-clean', action='store_true', help='跳过清理步骤')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== 云盘后端Linux打包工具 ===")
|
||||
print(f"当前目录: {os.getcwd()}")
|
||||
|
||||
# 检查环境
|
||||
check_python_version()
|
||||
check_dependencies()
|
||||
|
||||
# 清理构建目录
|
||||
if args.clean:
|
||||
clean_build_dirs()
|
||||
print("[OK] 清理完成")
|
||||
return
|
||||
|
||||
if not args.no_clean:
|
||||
clean_build_dirs()
|
||||
|
||||
# 创建规格文件
|
||||
create_spec_file()
|
||||
|
||||
# 运行打包
|
||||
run_pyinstaller()
|
||||
|
||||
# 创建部署包
|
||||
create_deployment_package()
|
||||
|
||||
print("\n=== 打包完成 ===")
|
||||
print("部署包位置: ./deploy/")
|
||||
print("请查看 ./deploy/README.md 了解部署说明")
|
||||
print("\n安装方式:")
|
||||
print("1. 用户级安装(推荐):./install_user.sh")
|
||||
print("2. 系统级安装:sudo ./install.sh")
|
||||
print("3. 直接运行:./cloud-drive-server")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,242 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
适用于无共享库Python环境的打包脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
|
||||
def check_python_version():
|
||||
"""检查Python版本"""
|
||||
if sys.version_info < (3, 8):
|
||||
print("错误: 需要Python 3.8或更高版本")
|
||||
sys.exit(1)
|
||||
print(f"[OK] Python版本: {sys.version}")
|
||||
|
||||
def check_dependencies():
|
||||
"""检查必要的依赖"""
|
||||
try:
|
||||
import PyInstaller
|
||||
print(f"[OK] PyInstaller版本: {PyInstaller.__version__}")
|
||||
except ImportError:
|
||||
print("错误: 未安装PyInstaller")
|
||||
print("请运行: pip install pyinstaller")
|
||||
sys.exit(1)
|
||||
|
||||
def clean_build_dirs():
|
||||
"""清理之前的构建目录"""
|
||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
||||
for dir_name in dirs_to_clean:
|
||||
if os.path.exists(dir_name):
|
||||
print(f"清理目录: {dir_name}")
|
||||
shutil.rmtree(dir_name)
|
||||
|
||||
# 清理Python缓存文件
|
||||
for root, dirs, files in os.walk('.'):
|
||||
for file in files:
|
||||
if file.endswith('.pyc') or file.endswith('.pyo'):
|
||||
os.remove(os.path.join(root, file))
|
||||
if '__pycache__' in dirs:
|
||||
shutil.rmtree(os.path.join(root, '__pycache__'))
|
||||
|
||||
def run_pyinstaller_noshared():
|
||||
"""运行PyInstaller进行打包(无共享库版本)"""
|
||||
print("开始打包(无共享库模式)...")
|
||||
|
||||
# 尝试多种打包方式
|
||||
spec_files = ['build_noshared.spec', 'build.spec']
|
||||
|
||||
for spec_file in spec_files:
|
||||
if os.path.exists(spec_file):
|
||||
print(f"使用规格文件: {spec_file}")
|
||||
|
||||
# 构建命令
|
||||
cmd = ['pyinstaller', '--clean', '--noupx', spec_file]
|
||||
|
||||
# 如果是build.spec,添加额外参数
|
||||
if spec_file == 'build.spec':
|
||||
cmd.extend(['--noupx', '--debug', 'imports'])
|
||||
|
||||
try:
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
print(f"[OK] PyInstaller执行成功 (使用 {spec_file})")
|
||||
if result.stdout:
|
||||
print("输出:", result.stdout)
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"使用 {spec_file} 打包失败: {e}")
|
||||
if e.stderr:
|
||||
print("错误输出:", e.stderr)
|
||||
continue
|
||||
|
||||
print("错误: 所有打包方式都失败了")
|
||||
return False
|
||||
|
||||
def create_simple_package():
|
||||
"""创建简单的部署包(不使用PyInstaller)"""
|
||||
print("创建简单部署包...")
|
||||
|
||||
deploy_dir = Path('deploy')
|
||||
if deploy_dir.exists():
|
||||
shutil.rmtree(deploy_dir)
|
||||
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 复制源代码
|
||||
shutil.copytree('app', deploy_dir / 'app')
|
||||
shutil.copy2('main.py', deploy_dir)
|
||||
shutil.copy2('requirements.txt', deploy_dir)
|
||||
shutil.copy2('.env.example', deploy_dir)
|
||||
|
||||
# 创建启动脚本
|
||||
startup_script = '''#!/bin/bash
|
||||
# 云盘后端服务启动脚本(Python模式)
|
||||
|
||||
# 进入脚本所在目录
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 检查Python环境
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "错误: 未找到python3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查虚拟环境
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "创建虚拟环境..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
else
|
||||
echo "激活虚拟环境..."
|
||||
source venv/bin/activate
|
||||
fi
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "警告: .env 文件不存在,将使用默认配置"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example .env
|
||||
echo "已复制 .env.example 为 .env,请根据需要修改配置"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
echo "启动云盘后端服务..."
|
||||
python main.py
|
||||
'''
|
||||
|
||||
script_path = deploy_dir / 'start.sh'
|
||||
with open(script_path, 'w', encoding='utf-8') as f:
|
||||
f.write(startup_script)
|
||||
|
||||
os.chmod(script_path, 0o755)
|
||||
|
||||
# 创建安装脚本
|
||||
install_script = '''#!/bin/bash
|
||||
# 简单安装脚本
|
||||
|
||||
INSTALL_DIR="$HOME/cloud-drive"
|
||||
|
||||
echo "=== 云盘后端服务安装(简单版本) ==="
|
||||
|
||||
# 创建安装目录
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# 复制文件
|
||||
cp -r * "$INSTALL_DIR/"
|
||||
cd "$INSTALL_DIR"
|
||||
|
||||
# 设置权限
|
||||
chmod +x start.sh
|
||||
|
||||
echo "=== 安装完成 ==="
|
||||
echo "进入目录: cd $INSTALL_DIR"
|
||||
echo "启动服务: ./start.sh"
|
||||
'''
|
||||
|
||||
install_path = deploy_dir / 'install_simple.sh'
|
||||
with open(install_path, 'w', encoding='utf-8') as f:
|
||||
f.write(install_script)
|
||||
|
||||
os.chmod(install_path, 0o755)
|
||||
|
||||
print(f"[OK] 简单部署包创建完成: {deploy_dir.absolute()}")
|
||||
return True
|
||||
|
||||
def create_deployment_package():
|
||||
"""创建部署包"""
|
||||
dist_dir = Path('dist')
|
||||
deploy_dir = Path('deploy')
|
||||
|
||||
if deploy_dir.exists():
|
||||
shutil.rmtree(deploy_dir)
|
||||
|
||||
deploy_dir.mkdir()
|
||||
|
||||
# 尝试复制可执行文件
|
||||
exe_file = dist_dir / 'cloud-drive-server'
|
||||
if exe_file.exists():
|
||||
shutil.copy2(exe_file, deploy_dir)
|
||||
print(f"[OK] 复制可执行文件到 {deploy_dir}")
|
||||
return True
|
||||
else:
|
||||
print("警告: 未找到可执行文件,创建简单部署包")
|
||||
return create_simple_package()
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
parser = argparse.ArgumentParser(description='云盘后端Linux打包工具(无共享库版)')
|
||||
parser.add_argument('--clean', action='store_true', help='仅清理构建目录')
|
||||
parser.add_argument('--no-clean', action='store_true', help='跳过清理步骤')
|
||||
parser.add_argument('--simple', action='store_true', help='创建简单部署包(不使用PyInstaller)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== 云盘后端Linux打包工具(无共享库版) ===")
|
||||
print(f"当前目录: {os.getcwd()}")
|
||||
|
||||
# 检查环境
|
||||
check_python_version()
|
||||
check_dependencies()
|
||||
|
||||
# 清理构建目录
|
||||
if args.clean:
|
||||
clean_build_dirs()
|
||||
print("[OK] 清理完成")
|
||||
return
|
||||
|
||||
if not args.no_clean:
|
||||
clean_build_dirs()
|
||||
|
||||
if args.simple:
|
||||
# 直接创建简单部署包
|
||||
create_simple_package()
|
||||
else:
|
||||
# 尝试PyInstaller打包
|
||||
if run_pyinstaller_noshared():
|
||||
create_deployment_package()
|
||||
else:
|
||||
print("PyInstaller打包失败,创建简单部署包...")
|
||||
create_simple_package()
|
||||
|
||||
print("\n=== 打包完成 ===")
|
||||
print("部署包位置: ./deploy/")
|
||||
|
||||
# 检查部署包内容
|
||||
deploy_dir = Path('deploy')
|
||||
if (deploy_dir / 'cloud-drive-server').exists():
|
||||
print("✓ 可执行文件: cloud-drive-server")
|
||||
print("运行方式: ./cloud-drive-server")
|
||||
else:
|
||||
print("✓ Python源代码包")
|
||||
print("运行方式: ./start.sh")
|
||||
print("安装方式: ./install_simple.sh")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 使用Docker构建Linux可执行文件
|
||||
|
||||
echo "=== 使用Docker构建Linux可执行文件 ==="
|
||||
|
||||
# 构建Docker镜像
|
||||
echo "构建Docker镜像..."
|
||||
docker build -f Dockerfile.build -t cloud-drive-builder .
|
||||
|
||||
# 运行构建容器并提取结果
|
||||
echo "运行构建..."
|
||||
docker run --rm -v $(pwd):/output cloud-drive-builder bash -c "cp -r /opt/cloud-drive/* /output/"
|
||||
|
||||
echo "=== 构建完成 ==="
|
||||
echo "Linux可执行文件已生成到当前目录"
|
||||
echo "文件列表:"
|
||||
ls -la cloud-drive-server start.sh README.md .env.example
|
||||
|
||||
echo ""
|
||||
echo "部署文件已准备就绪,可以上传到Linux服务器"
|
||||
echo "建议下一步:"
|
||||
echo "1. 将所有文件上传到Linux服务器"
|
||||
echo "2. 运行 sudo ./install.sh 进行安装"
|
||||
@@ -1,219 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
检查数据库files表和实际文件存储情况
|
||||
"""
|
||||
|
||||
import mysql.connector
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
def check_database_files():
|
||||
"""检查数据库中的文件记录"""
|
||||
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 查询files表中的所有记录
|
||||
cursor.execute("""
|
||||
SELECT id, user_id, original_filename, filename, file_path, file_size,
|
||||
file_hash, mime_type, created_at
|
||||
FROM files
|
||||
ORDER BY created_at DESC
|
||||
""")
|
||||
|
||||
files = cursor.fetchall()
|
||||
|
||||
print("=== 数据库 files 表中的记录 ===")
|
||||
if files:
|
||||
for file in files:
|
||||
(id, user_id, original_filename, filename, file_path,
|
||||
file_size, file_hash, mime_type, created_at) = file
|
||||
|
||||
print(f"ID: {id}")
|
||||
print(f" 用户ID: {user_id}")
|
||||
print(f" 原始文件名: {original_filename}")
|
||||
print(f" 存储文件名: {filename}")
|
||||
print(f" 文件路径: {file_path}")
|
||||
print(f" 文件大小: {file_size} bytes")
|
||||
print(f" 文件哈希: {file_hash}")
|
||||
print(f" MIME类型: {mime_type}")
|
||||
print(f" 创建时间: {created_at}")
|
||||
print("-" * 50)
|
||||
else:
|
||||
print("files 表中没有记录")
|
||||
|
||||
print(f"\n总记录数: {len(files)}")
|
||||
|
||||
# 检查每个文件是否真实存在
|
||||
print("\n=== 文件存在性检查 ===")
|
||||
existing_count = 0
|
||||
missing_files = []
|
||||
|
||||
for file in files:
|
||||
(id, user_id, original_filename, filename, file_path,
|
||||
file_size, file_hash, mime_type, created_at) = file
|
||||
|
||||
full_path = os.path.join("uploads", filename)
|
||||
if os.path.exists(full_path):
|
||||
existing_count += 1
|
||||
print(f"✅ ID {id}: {original_filename} - 文件存在")
|
||||
|
||||
# 检查文件大小
|
||||
actual_size = os.path.getsize(full_path)
|
||||
if actual_size != file_size:
|
||||
print(f" ⚠️ 文件大小不匹配! 数据库: {file_size}, 实际: {actual_size}")
|
||||
|
||||
# 检查文件哈希
|
||||
try:
|
||||
with open(full_path, 'rb') as f:
|
||||
content = f.read()
|
||||
actual_hash = hashlib.sha256(content).hexdigest()
|
||||
if actual_hash != file_hash:
|
||||
print(f" ❌ 文件哈希不匹配! 数据库: {file_hash}")
|
||||
print(f" 实际: {actual_hash}")
|
||||
except Exception as e:
|
||||
print(f" ❌ 无法读取文件或计算哈希: {e}")
|
||||
|
||||
else:
|
||||
missing_files.append((id, original_filename, filename))
|
||||
print(f"❌ ID {id}: {original_filename} - 文件不存在!")
|
||||
|
||||
print(f"\n实际存在的文件: {existing_count}")
|
||||
print(f"缺失的文件: {len(missing_files)}")
|
||||
|
||||
if missing_files:
|
||||
print("\n缺失文件详情:")
|
||||
for (id, original_filename, filename) in missing_files:
|
||||
print(f" ID {id}: {original_filename} (应存储为: {filename})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"数据库查询出错: {e}")
|
||||
finally:
|
||||
if 'conn' in locals() and conn.is_connected():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def check_uploads_directory():
|
||||
"""检查uploads目录中的实际文件"""
|
||||
|
||||
print("\n=== uploads 目录中的实际文件 ===")
|
||||
|
||||
uploads_dir = "uploads"
|
||||
if os.path.exists(uploads_dir):
|
||||
files = os.listdir(uploads_dir)
|
||||
if files:
|
||||
print(f"目录: {uploads_dir}")
|
||||
print(f"文件数量: {len(files)}")
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(uploads_dir, file)
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# 计算文件哈希
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read()
|
||||
file_hash = hashlib.sha256(content).hexdigest()
|
||||
|
||||
# 尝试读取文本内容
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
text_content = f.read()
|
||||
content_preview = text_content[:100] + "..." if len(text_content) > 100 else text_content
|
||||
content_preview = repr(content_preview) # 显示引号和特殊字符
|
||||
except:
|
||||
content_preview = "(二进制文件)"
|
||||
|
||||
except Exception as e:
|
||||
file_hash = f"无法计算哈希: {e}"
|
||||
content_preview = f"无法读取: {e}"
|
||||
|
||||
print(f"\n📄 {file}")
|
||||
print(f" 大小: {file_size} bytes")
|
||||
print(f" 哈希: {file_hash}")
|
||||
print(f" 内容预览: {content_preview}")
|
||||
else:
|
||||
print(f"目录 {uploads_dir} 为空")
|
||||
else:
|
||||
print(f"目录 {uploads_dir} 不存在")
|
||||
|
||||
def check_file_integrity():
|
||||
"""检查文件完整性,对比数据库和实际文件"""
|
||||
|
||||
print("\n=== 文件完整性检查 ===")
|
||||
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 查询所有文件
|
||||
cursor.execute("SELECT id, filename, file_hash, file_size FROM files")
|
||||
db_files = cursor.fetchall()
|
||||
|
||||
integrity_issues = []
|
||||
|
||||
for (id, filename, expected_hash, expected_size) in db_files:
|
||||
full_path = os.path.join("uploads", filename)
|
||||
|
||||
if os.path.exists(full_path):
|
||||
# 检查大小
|
||||
actual_size = os.path.getsize(full_path)
|
||||
if actual_size != expected_size:
|
||||
integrity_issues.append(f"ID {id}: 文件大小不匹配 (期望: {expected_size}, 实际: {actual_size})")
|
||||
continue
|
||||
|
||||
# 检查哈希
|
||||
try:
|
||||
with open(full_path, 'rb') as f:
|
||||
content = f.read()
|
||||
actual_hash = hashlib.sha256(content).hexdigest()
|
||||
|
||||
if actual_hash != expected_hash:
|
||||
integrity_issues.append(f"ID {id}: 文件哈希不匹配")
|
||||
print(f" 期望哈希: {expected_hash}")
|
||||
print(f" 实际哈希: {actual_hash}")
|
||||
|
||||
except Exception as e:
|
||||
integrity_issues.append(f"ID {id}: 无法计算文件哈希 - {e}")
|
||||
else:
|
||||
integrity_issues.append(f"ID {id}: 文件不存在")
|
||||
|
||||
if integrity_issues:
|
||||
print(f"❌ 发现 {len(integrity_issues)} 个完整性问题:")
|
||||
for issue in integrity_issues:
|
||||
print(f" - {issue}")
|
||||
else:
|
||||
print("✅ 所有文件完整性检查通过!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"完整性检查出错: {e}")
|
||||
finally:
|
||||
if 'conn' in locals() and conn.is_connected():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_database_files()
|
||||
check_uploads_directory()
|
||||
check_file_integrity()
|
||||
|
||||
print("\n=== 总结 ===")
|
||||
print("文件存储情况:")
|
||||
print("1. 数据库存储文件的元数据信息")
|
||||
print("2. 实际文件存储在 backend/uploads/ 目录")
|
||||
print("3. 文件名使用UUID格式确保唯一性")
|
||||
print("4. 通过file_hash确保文件完整性")
|
||||
print("5. 支持文件去重功能")
|
||||
@@ -1,51 +0,0 @@
|
||||
import pymysql
|
||||
from app.core.config import settings
|
||||
|
||||
def check_user_login_table():
|
||||
try:
|
||||
# 解析连接字符串
|
||||
import re
|
||||
pattern = r'mysql\+pymysql://([^:]+):([^@]+)@([^:]+):(\d+)/(.+)'
|
||||
match = re.match(pattern, settings.DATABASE_URL)
|
||||
|
||||
if match:
|
||||
username, password, host, port, database = match.groups()
|
||||
|
||||
connection = pymysql.connect(
|
||||
host=host,
|
||||
port=int(port),
|
||||
user=username,
|
||||
password=password,
|
||||
database=database,
|
||||
charset='utf8mb4'
|
||||
)
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
# 检查user_login表结构
|
||||
print("=== user_login表结构 ===")
|
||||
cursor.execute("DESCRIBE user_login")
|
||||
columns = cursor.fetchall()
|
||||
for column in columns:
|
||||
print(f"{column[0]}: {column[1]} {column[2]} {column[3]} {column[4]}")
|
||||
|
||||
print("\n=== user_login表数据示例 ===")
|
||||
cursor.execute("SELECT * FROM user_login LIMIT 3")
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
print(row)
|
||||
|
||||
print("\n=== 检查是否有用户相关的表 ===")
|
||||
cursor.execute("SHOW TABLES LIKE '%user%'")
|
||||
user_tables = cursor.fetchall()
|
||||
for table in user_tables:
|
||||
print(f"- {table[0]}")
|
||||
|
||||
connection.close()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"检查表结构失败: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_user_login_table()
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
print("Starting Cloud Drive Application Server...")
|
||||
|
||||
import sys
|
||||
print(f"Python version: {sys.version}")
|
||||
|
||||
try:
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
print("FastAPI dependencies available")
|
||||
|
||||
app = FastAPI(
|
||||
title="Cloud Drive API",
|
||||
description="Modern Cloud Storage Web Application Backend API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "Cloud Drive API",
|
||||
"version": "1.0.1",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "healthy"}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def api_health():
|
||||
import time
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@app.get("/test")
|
||||
async def test():
|
||||
return {"test": "ok", "server": "working"}
|
||||
|
||||
try:
|
||||
from app.core.config import settings
|
||||
print("Full app module available")
|
||||
mode = "full"
|
||||
except ImportError:
|
||||
print("Using simplified mode")
|
||||
mode = "simplified"
|
||||
|
||||
@app.get("/info")
|
||||
async def info():
|
||||
return {
|
||||
"mode": mode,
|
||||
"python_version": str(sys.version),
|
||||
"status": "running"
|
||||
}
|
||||
|
||||
print("=" * 50)
|
||||
print("Server URLs:")
|
||||
print(" http://localhost:8080")
|
||||
print(" http://localhost:8080/docs")
|
||||
print(" http://localhost:8080/api/v1/health")
|
||||
print("=" * 50)
|
||||
print("Press Ctrl+C to stop server")
|
||||
print()
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
|
||||
except ImportError as e:
|
||||
print(f"Dependency import failed: {e}")
|
||||
print("Please run: pip install fastapi uvicorn")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"Startup failed: {e}")
|
||||
sys.exit(1)
|
||||
@@ -1,20 +0,0 @@
|
||||
from app.core.database import SessionLocal
|
||||
from app.models.user import User
|
||||
|
||||
def cleanup_test_user():
|
||||
"""清理测试用户"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# 删除测试用户
|
||||
test_user = db.query(User).filter(User.username == "peng").first()
|
||||
if test_user:
|
||||
db.delete(test_user)
|
||||
db.commit()
|
||||
print("已删除测试用户 'peng'")
|
||||
else:
|
||||
print("未找到测试用户 'peng'")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
cleanup_test_user()
|
||||
@@ -1,33 +0,0 @@
|
||||
from sqlalchemy import create_engine, text
|
||||
from app.core.config import settings
|
||||
from app.core.database import Base
|
||||
from app.models import User
|
||||
|
||||
def create_user_table():
|
||||
try:
|
||||
print(f"连接数据库: {settings.DATABASE_URL}")
|
||||
|
||||
# 创建数据库引擎
|
||||
engine = create_engine(settings.DATABASE_URL, echo=True)
|
||||
|
||||
# 创建所有表
|
||||
Base.metadata.create_all(bind=engine)
|
||||
print("users表创建成功!")
|
||||
|
||||
# 检查表是否创建成功
|
||||
with engine.connect() as conn:
|
||||
result = conn.execute(text("DESCRIBE users"))
|
||||
columns = result.fetchall()
|
||||
print("\nusers表结构:")
|
||||
for column in columns:
|
||||
print(f" {column[0]}: {column[1]} {column[2]} {column[3]} {column[4]}")
|
||||
|
||||
print("\n数据库表创建完成!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"创建表失败: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_user_table()
|
||||
@@ -1,71 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
创建测试用户的脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import hashlib
|
||||
import mysql.connector
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def create_user_directly():
|
||||
"""直接在数据库中创建用户"""
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 检查用户是否已存在
|
||||
cursor.execute("SELECT id FROM users WHERE username = %s", ("testuser",))
|
||||
user = cursor.fetchone()
|
||||
|
||||
if user:
|
||||
print(f"用户已存在,ID: {user[0]}")
|
||||
return user[0]
|
||||
|
||||
# 创建密码哈希
|
||||
password = "TestPass123!"
|
||||
password_hash = hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
# 插入用户
|
||||
insert_query = """
|
||||
INSERT INTO users (username, email, password_hash, storage_quota, storage_used, is_active, is_verified)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||
"""
|
||||
cursor.execute(insert_query, (
|
||||
"testuser",
|
||||
"test@example.com",
|
||||
password_hash,
|
||||
104857600, # 100MB
|
||||
0,
|
||||
True,
|
||||
True
|
||||
))
|
||||
|
||||
user_id = cursor.lastrowid
|
||||
conn.commit()
|
||||
|
||||
print(f"用户创建成功,ID: {user_id}")
|
||||
return user_id
|
||||
|
||||
except Exception as e:
|
||||
print(f"创建用户出错: {e}")
|
||||
return None
|
||||
finally:
|
||||
if 'conn' in locals() and conn.is_connected():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
user_id = create_user_directly()
|
||||
if user_id:
|
||||
print(f"测试用户ID: {user_id}")
|
||||
else:
|
||||
print("创建用户失败")
|
||||
@@ -1,217 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
调试文件下载接口的脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import mysql.connector
|
||||
import os
|
||||
import json
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def debug_download_issue():
|
||||
"""调试下载接口问题"""
|
||||
|
||||
print("=== 调试文件下载接口 ===")
|
||||
|
||||
# 检查数据库中的文件信息
|
||||
try:
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 查询所有文件
|
||||
cursor.execute("""
|
||||
SELECT id, user_id, original_filename, filename, file_path, file_size,
|
||||
file_hash, mime_type, created_at
|
||||
FROM files
|
||||
ORDER BY id
|
||||
""")
|
||||
files = cursor.fetchall()
|
||||
|
||||
print("数据库中的文件记录:")
|
||||
for file in files:
|
||||
(id, user_id, original_filename, filename, file_path,
|
||||
file_size, file_hash, mime_type, created_at) = file
|
||||
print(f"ID: {id}, 用户ID: {user_id}, 文件名: {original_filename}")
|
||||
print(f" 存储路径: {file_path}")
|
||||
print(f" 文件大小: {file_size} bytes")
|
||||
print(f" 创建时间: {created_at}")
|
||||
print("-" * 50)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"数据库查询出错: {e}")
|
||||
return
|
||||
|
||||
# 测试特定文件下载
|
||||
test_cases = [
|
||||
{"user_id": 3, "file_id": 2, "description": "用户3下载文件2(axurerp-48.png)"},
|
||||
{"user_id": 3, "file_id": 3, "description": "用户3下载文件3(axurerp-128.png)"},
|
||||
{"user_id": 8, "file_id": 4, "description": "用户8下载文件4(hash_demo.txt)"},
|
||||
{"user_id": 3, "file_id": 4, "description": "用户3下载文件4(权限测试)"},
|
||||
{"user_id": 1, "file_id": 2, "description": "用户1下载文件2(不存在用户)"},
|
||||
{"user_id": 3, "file_id": 999, "description": "用户3下载文件999(不存在文件)"},
|
||||
]
|
||||
|
||||
for test_case in test_cases:
|
||||
user_id = test_case["user_id"]
|
||||
file_id = test_case["file_id"]
|
||||
description = test_case["description"]
|
||||
|
||||
print(f"\n=== 测试: {description} ===")
|
||||
print(f"入参: user_id={user_id}, file_id={file_id}")
|
||||
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/download",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"HTTP状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
# 下载成功
|
||||
content_length = len(response.content)
|
||||
print(f"下载成功! 文件大小: {content_length} bytes")
|
||||
|
||||
# 保存下载的文件用于检查
|
||||
save_filename = f"downloaded_user{user_id}_file{file_id}"
|
||||
if content_length > 0:
|
||||
with open(save_filename, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f"文件已保存为: {save_filename}")
|
||||
else:
|
||||
print("警告: 下载的文件为空")
|
||||
|
||||
# 显示内容预览
|
||||
try:
|
||||
if response.headers.get('content-type', '').startswith('text/'):
|
||||
text_content = response.content.decode('utf-8')
|
||||
preview = text_content[:100] + "..." if len(text_content) > 100 else text_content
|
||||
print(f"内容预览: {preview}")
|
||||
else:
|
||||
print("二进制文件,无法预览内容")
|
||||
except:
|
||||
print("无法预览文件内容")
|
||||
|
||||
else:
|
||||
# 下载失败
|
||||
print(f"下载失败!")
|
||||
print(f"响应内容: {response.text}")
|
||||
|
||||
# 尝试解析JSON错误信息
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"错误详情: {json.dumps(error_data, indent=2, ensure_ascii=False)}")
|
||||
except:
|
||||
print("无法解析错误响应")
|
||||
|
||||
except Exception as e:
|
||||
print(f"请求出错: {e}")
|
||||
|
||||
def check_file_access_permission():
|
||||
"""检查文件访问权限"""
|
||||
|
||||
print("\n=== 检查文件访问权限 ===")
|
||||
|
||||
# 检查uploads目录权限
|
||||
uploads_dir = "uploads"
|
||||
if os.path.exists(uploads_dir):
|
||||
print(f"uploads目录存在: {os.path.abspath(uploads_dir)}")
|
||||
|
||||
# 检查目录权限
|
||||
try:
|
||||
test_file = os.path.join(uploads_dir, "test_permission.txt")
|
||||
with open(test_file, 'w') as f:
|
||||
f.write("test")
|
||||
|
||||
if os.path.exists(test_file):
|
||||
os.remove(test_file)
|
||||
print("uploads目录读写权限正常")
|
||||
else:
|
||||
print("uploads目录权限异常")
|
||||
except Exception as e:
|
||||
print(f"无法在uploads目录写入文件: {e}")
|
||||
else:
|
||||
print("uploads目录不存在")
|
||||
|
||||
# 检查数据库文件记录与实际文件的对应关系
|
||||
try:
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT id, user_id, filename, file_path FROM files ORDER BY id")
|
||||
files = cursor.fetchall()
|
||||
|
||||
print("\n文件记录与实际文件对应关系:")
|
||||
for (id, user_id, filename, file_path) in files:
|
||||
full_path = os.path.join("uploads", filename)
|
||||
exists = os.path.exists(full_path)
|
||||
if exists:
|
||||
size = os.path.getsize(full_path)
|
||||
print(f"ID {id}: 文件存在 ({size} bytes)")
|
||||
else:
|
||||
print(f"ID {id}: 文件不存在 - {full_path}")
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"检查文件对应关系出错: {e}")
|
||||
|
||||
def test_download_with_curl():
|
||||
"""使用curl测试下载接口"""
|
||||
|
||||
print("\n=== 使用curl测试下载接口 ===")
|
||||
|
||||
test_cases = [
|
||||
{"user_id": 3, "file_id": 2},
|
||||
{"user_id": 3, "file_id": 3},
|
||||
]
|
||||
|
||||
for test_case in test_cases:
|
||||
user_id = test_case["user_id"]
|
||||
file_id = test_case["file_id"]
|
||||
|
||||
print(f"\n测试 curl - 用户ID: {user_id}, 文件ID: {file_id}")
|
||||
|
||||
curl_command = f'''curl -X POST "{BASE_URL}/files/download" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{{"user_id": {user_id}, "file_id": {file_id}}}' \\
|
||||
-v'''
|
||||
|
||||
print("命令:")
|
||||
print(curl_command)
|
||||
print("(请在终端中手动执行此命令)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_download_issue()
|
||||
check_file_access_permission()
|
||||
test_download_with_curl()
|
||||
|
||||
print("\n=== 调试总结 ===")
|
||||
print("如果下载失败,可能的原因:")
|
||||
print("1. 文件不存在或已被删除")
|
||||
print("2. 用户ID与文件不匹配")
|
||||
print("3. uploads目录权限问题")
|
||||
print("4. 实际文件大小为0字节")
|
||||
print("5. 下载接口实现逻辑问题")
|
||||
@@ -1,150 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
详细调试下载接口的每个步骤
|
||||
"""
|
||||
|
||||
import requests
|
||||
import os
|
||||
import mysql.connector
|
||||
|
||||
def debug_download_step_by_step():
|
||||
"""逐步调试下载过程"""
|
||||
|
||||
print("=== 详细下载接口调试 ===")
|
||||
|
||||
# 测试参数
|
||||
user_id = 3
|
||||
file_id = 22
|
||||
|
||||
print(f"测试参数: user_id={user_id}, file_id={file_id}")
|
||||
|
||||
# 步骤1: 检查数据库中的文件记录
|
||||
print("\n--- 步骤1: 检查数据库记录 ---")
|
||||
try:
|
||||
conn = mysql.connector.connect(
|
||||
host='101.126.85.76',
|
||||
user='mytest_db',
|
||||
password='mytest_db',
|
||||
database='mytest_db'
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
SELECT id, user_id, original_filename, filename, file_path, file_size, mime_type
|
||||
FROM files
|
||||
WHERE id = %s AND user_id = %s
|
||||
''', (file_id, user_id))
|
||||
|
||||
file_record = cursor.fetchone()
|
||||
|
||||
if file_record:
|
||||
id, db_user_id, original_filename, filename, file_path, file_size, mime_type = file_record
|
||||
print(f"✅ 数据库记录找到:")
|
||||
print(f" 文件ID: {id}")
|
||||
print(f" 用户ID: {db_user_id}")
|
||||
print(f" 原始文件名: {original_filename}")
|
||||
print(f" 存储文件名: {filename}")
|
||||
print(f" 文件路径: {file_path}")
|
||||
print(f" 文件大小: {file_size} bytes")
|
||||
print(f" MIME类型: {mime_type}")
|
||||
else:
|
||||
print("❌ 数据库中没有找到匹配的记录")
|
||||
return
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 数据库查询失败: {e}")
|
||||
return
|
||||
|
||||
# 步骤2: 检查实际文件是否存在
|
||||
print("\n--- 步骤2: 检查实际文件 ---")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
actual_size = os.path.getsize(file_path)
|
||||
print(f"✅ 文件存在:")
|
||||
print(f" 路径: {os.path.abspath(file_path)}")
|
||||
print(f" 实际大小: {actual_size} bytes")
|
||||
|
||||
if actual_size == file_size:
|
||||
print("✅ 文件大小匹配数据库记录")
|
||||
else:
|
||||
print(f"⚠️ 文件大小不匹配: 数据库{file_size} vs 实际{actual_size}")
|
||||
|
||||
# 检查文件可读性
|
||||
if os.access(file_path, os.R_OK):
|
||||
print("✅ 文件可读")
|
||||
|
||||
# 读取文件内容验证
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
print(f"✅ 文件内容可读,长度: {len(content)} 字符")
|
||||
print(f" 内容预览: {content[:50]}...")
|
||||
except Exception as e:
|
||||
print(f"❌ 读取文件内容失败: {e}")
|
||||
else:
|
||||
print("❌ 文件不可读 - 权限问题")
|
||||
else:
|
||||
print(f"❌ 文件不存在: {file_path}")
|
||||
return
|
||||
|
||||
# 步骤3: 测试API下载请求
|
||||
print("\n--- 步骤3: 测试API下载请求 ---")
|
||||
|
||||
try:
|
||||
download_data = {'user_id': user_id, 'file_id': file_id}
|
||||
|
||||
print(f"发送请求: POST /api/v1/files/download")
|
||||
print(f"请求体: {download_data}")
|
||||
|
||||
response = requests.post(
|
||||
'http://localhost:8000/api/v1/files/download',
|
||||
json=download_data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
print(f"响应头: {dict(response.headers)}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ 下载成功!")
|
||||
|
||||
content_type = response.headers.get('content-type', '')
|
||||
content_length = len(response.content)
|
||||
|
||||
print(f" 响应类型: {content_type}")
|
||||
print(f" 内容长度: {content_length} bytes")
|
||||
|
||||
if content_type.startswith('text/'):
|
||||
text_content = response.text
|
||||
print(f" 文本内容长度: {len(text_content)} 字符")
|
||||
print(f" 文本内容预览: {text_content[:50]}...")
|
||||
|
||||
# 验证内容
|
||||
if len(text_content) == file_size / 2: # 大约UTF-8字符数
|
||||
print("✅ 内容大小预期范围内")
|
||||
else:
|
||||
print(f"⚠️ 内容大小异常: 预期~{file_size//2}字符,实际{len(text_content)}字符")
|
||||
else:
|
||||
print(" 二进制内容,无法显示预览")
|
||||
|
||||
else:
|
||||
print("❌ 下载失败!")
|
||||
print(f"错误响应: {response.text}")
|
||||
|
||||
# 尝试解析JSON错误
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"错误详情: {error_data}")
|
||||
except:
|
||||
print("无法解析错误响应")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ API请求失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_download_step_by_step()
|
||||
@@ -1,48 +0,0 @@
|
||||
import sys
|
||||
import traceback
|
||||
from app.core.database import SessionLocal
|
||||
from app.services.user_service import UserService
|
||||
from app.schemas.auth import UserRegister
|
||||
|
||||
def debug_email_duplicate():
|
||||
"""调试邮箱重复问题"""
|
||||
try:
|
||||
print("=== 调试邮箱重复问题 ===")
|
||||
|
||||
db = SessionLocal()
|
||||
user_service = UserService(db)
|
||||
|
||||
# 先检查现有用户
|
||||
print("1. 检查现有用户...")
|
||||
existing_user = user_service.get_user_by_email("user@example.com")
|
||||
if existing_user:
|
||||
print(f" 找到现有用户: ID={existing_user.id}, 用户名={existing_user.username}, 邮箱={existing_user.email}")
|
||||
else:
|
||||
print(" 未找到现有用户")
|
||||
|
||||
# 尝试创建重复邮箱的用户
|
||||
print("\n2. 尝试创建重复邮箱的用户...")
|
||||
user_data = UserRegister(
|
||||
username="test_duplicate",
|
||||
email="user@example.com", # 重复邮箱
|
||||
password="TestPass123!",
|
||||
confirm_password="TestPass123!"
|
||||
)
|
||||
|
||||
try:
|
||||
user = user_service.create_user(user_data)
|
||||
print(f" 用户创建成功: {user.id}")
|
||||
except Exception as e:
|
||||
print(f" 用户创建失败: {e}")
|
||||
print(f" 异常类型: {type(e).__name__}")
|
||||
print(f" 异常详情: {e.detail if hasattr(e, 'detail') else '无详情'}")
|
||||
traceback.print_exc()
|
||||
|
||||
db.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"调试过程出错: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_email_duplicate()
|
||||
@@ -1,78 +0,0 @@
|
||||
import sys
|
||||
import traceback
|
||||
from app.core.database import SessionLocal
|
||||
from app.core.security import get_password_hash
|
||||
from app.models.user import User
|
||||
|
||||
def debug_password_hashing():
|
||||
"""测试密码哈希功能"""
|
||||
try:
|
||||
print("测试密码哈希功能...")
|
||||
password = "Stringst1@"
|
||||
hashed = get_password_hash(password)
|
||||
print(f"密码哈希成功: {hashed[:50]}...")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"密码哈希失败: {str(e)}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def debug_user_creation():
|
||||
"""测试用户创建"""
|
||||
try:
|
||||
print("\n测试用户创建...")
|
||||
|
||||
db = SessionLocal()
|
||||
|
||||
# 检查用户是否已存在
|
||||
existing_user = db.query(User).filter(User.username == "peng").first()
|
||||
if existing_user:
|
||||
print("用户 'peng' 已存在,删除旧记录...")
|
||||
db.delete(existing_user)
|
||||
db.commit()
|
||||
|
||||
# 创建新用户
|
||||
password_hash = get_password_hash("Stringst1@")
|
||||
print(f"密码哈希生成成功")
|
||||
|
||||
new_user = User(
|
||||
username="peng",
|
||||
email="user@example.com",
|
||||
password_hash=password_hash,
|
||||
is_active=True,
|
||||
is_verified=False
|
||||
)
|
||||
|
||||
db.add(new_user)
|
||||
db.commit()
|
||||
db.refresh(new_user)
|
||||
|
||||
print(f"用户创建成功! ID: {new_user.id}")
|
||||
|
||||
# 验证用户
|
||||
user = db.query(User).filter(User.username == "peng").first()
|
||||
if user:
|
||||
print(f"用户验证成功: {user.username}, {user.email}")
|
||||
|
||||
db.close()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"用户创建失败: {str(e)}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=== 调试用户注册问题 ===")
|
||||
|
||||
# 测试密码哈希
|
||||
if not debug_password_hashing():
|
||||
print("密码哈希有问题,退出")
|
||||
sys.exit(1)
|
||||
|
||||
# 测试用户创建
|
||||
if not debug_user_creation():
|
||||
print("用户创建有问题,退出")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n=== 调试完成,一切正常 ===")
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 带诊断信息的启动脚本
|
||||
|
||||
import socket
|
||||
import sys
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
|
||||
# 检查端口是否可用
|
||||
def check_port(port):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('0.0.0.0', port))
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
# 获取本机IP
|
||||
def get_local_ip():
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
return ip
|
||||
except:
|
||||
return "127.0.0.1"
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# 更宽松的CORS配置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/health"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "服务运行正常"
|
||||
}
|
||||
|
||||
@app.get("/debug")
|
||||
async def debug_info():
|
||||
return {
|
||||
"python_version": sys.version,
|
||||
"working_directory": ".",
|
||||
"available_endpoints": [
|
||||
"/",
|
||||
"/health",
|
||||
"/debug",
|
||||
"/docs",
|
||||
"/redoc",
|
||||
"/openapi.json"
|
||||
]
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = 8000
|
||||
|
||||
print("🔍 启动前诊断...")
|
||||
print(f"Python版本: {sys.version}")
|
||||
print(f"工作目录: {(await debug_info())['working_directory']}")
|
||||
|
||||
# 检查端口
|
||||
if not check_port(port):
|
||||
print(f"❌ 端口 {port} 被占用,尝试使用端口 8001")
|
||||
port = 8001
|
||||
|
||||
local_ip = get_local_ip()
|
||||
|
||||
print(f"🚀 启动云盘后端服务...")
|
||||
print("=" * 60)
|
||||
print(f"📍 本地访问: http://localhost:{port}")
|
||||
print(f"📍 网络访问: http://{local_ip}:{port}")
|
||||
print(f"📚 API文档: http://localhost:{port}/docs")
|
||||
print(f"📚 网络文档: http://{local_ip}:{port}/docs")
|
||||
print(f"❤️ 健康检查: http://localhost:{port}/health")
|
||||
print(f"🔧 调试信息: http://localhost:{port}/debug")
|
||||
print(f"⏹️ 按 Ctrl+C 停止服务")
|
||||
print("=" * 60)
|
||||
|
||||
# 启动时打印所有路由
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
print("\n📋 可用路由:")
|
||||
for route in app.routes:
|
||||
if hasattr(route, 'path') and hasattr(route, 'methods'):
|
||||
print(f" {list(route.methods)} {route.path}")
|
||||
print()
|
||||
|
||||
try:
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0", # 允许外部访问
|
||||
port=port,
|
||||
reload=False,
|
||||
access_log=True, # 显示访问日志
|
||||
log_level="info"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
print("\n💡 尝试的解决方案:")
|
||||
print("1. 检查防火墙设置")
|
||||
print("2. 尝试其他端口: python debug_start.py")
|
||||
print("3. 检查是否有其他程序占用端口")
|
||||
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
逐步调试文件上传过程的每个步骤
|
||||
"""
|
||||
|
||||
import requests
|
||||
import io
|
||||
import os
|
||||
|
||||
def debug_upload_step_by_step():
|
||||
"""逐步调试上传过程"""
|
||||
|
||||
print("=== 步骤1: 准备测试文件 ===")
|
||||
test_content = b"Debug upload content - step by step analysis"
|
||||
print(f"原始内容大小: {len(test_content)} bytes")
|
||||
print(f"原始内容: {test_content}")
|
||||
print()
|
||||
|
||||
print("=== 步骤2: 创建模拟上传文件 ===")
|
||||
file_obj = io.BytesIO(test_content)
|
||||
file_obj.seek(0)
|
||||
print(f"BytesIO对象创建成功")
|
||||
print(f"当前位置: {file_obj.tell()}")
|
||||
print()
|
||||
|
||||
print("=== 步骤3: 准备请求数据 ===")
|
||||
files = {
|
||||
'file': ('debug_step_test.txt', file_obj, 'text/plain')
|
||||
}
|
||||
data = {
|
||||
'user_id': 3,
|
||||
'description': 'Step by step debug test',
|
||||
'tags': 'debug,step',
|
||||
'is_public': 'false'
|
||||
}
|
||||
print("请求数据准备完成")
|
||||
print()
|
||||
|
||||
print("=== 步骤4: 发送上传请求 ===")
|
||||
try:
|
||||
response = requests.post('http://localhost:8000/api/v1/files/upload', files=files, data=data)
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
print(f"响应头: {dict(response.headers)}")
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
print("=== 步骤5: 上传成功分析 ===")
|
||||
if result.get('success'):
|
||||
file_info = result['data']['file']
|
||||
file_id = file_info['id']
|
||||
filename = file_info['filename']
|
||||
db_size = file_info['file_size']
|
||||
|
||||
print(f"数据库记录:")
|
||||
print(f" 文件ID: {file_id}")
|
||||
print(f" 存储文件名: {filename}")
|
||||
print(f" 数据库大小: {db_size} bytes")
|
||||
print(f" 预期大小: {len(test_content)} bytes")
|
||||
|
||||
# 检查磁盘文件
|
||||
print("\n=== 步骤6: 磁盘文件分析 ===")
|
||||
file_path = os.path.join('uploads', filename)
|
||||
print(f"预期路径: {file_path}")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
actual_size = os.path.getsize(file_path)
|
||||
print(f"实际文件大小: {actual_size} bytes")
|
||||
|
||||
if actual_size == 0:
|
||||
print("❌ 文件损坏: 大小为0")
|
||||
elif actual_size == len(test_content):
|
||||
print("✅ 文件完整")
|
||||
|
||||
# 验证内容
|
||||
with open(file_path, 'rb') as f:
|
||||
actual_content = f.read()
|
||||
|
||||
if actual_content == test_content:
|
||||
print("✅ 内容完全匹配")
|
||||
else:
|
||||
print("❌ 内容不匹配")
|
||||
print(f"预期: {test_content}")
|
||||
print(f"实际: {actual_content}")
|
||||
else:
|
||||
print(f"⚠️ 大小不匹配: {actual_size} != {len(test_content)}")
|
||||
|
||||
# 读取部分内容检查
|
||||
try:
|
||||
with open(file_path, 'rb') as f:
|
||||
actual_content = f.read(min(100, actual_size))
|
||||
print(f"实际内容预览: {actual_content}")
|
||||
except Exception as e:
|
||||
print(f"无法读取文件内容: {e}")
|
||||
else:
|
||||
print("❌ 文件不存在于磁盘")
|
||||
|
||||
# 检查目录
|
||||
upload_dir = 'uploads'
|
||||
if os.path.exists(upload_dir):
|
||||
print(f"uploads目录存在")
|
||||
files_in_dir = os.listdir(upload_dir)
|
||||
print(f"目录中的文件: {files_in_dir}")
|
||||
else:
|
||||
print("uploads目录不存在")
|
||||
|
||||
else:
|
||||
print("上传返回失败:", result)
|
||||
else:
|
||||
print("=== 步骤5: 上传失败分析 ===")
|
||||
print(f"HTTP状态码: {response.status_code}")
|
||||
print(f"响应内容: {response.text}")
|
||||
|
||||
# 尝试解析错误
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"错误详情: {error_data}")
|
||||
except:
|
||||
print("无法解析错误响应")
|
||||
|
||||
except Exception as e:
|
||||
print(f"请求异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_upload_step_by_step()
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
演示文件哈希原理和还原功能的脚本
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import requests
|
||||
import os
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
USER_ID = 8
|
||||
|
||||
def calculate_sha256_hash(file_content: bytes) -> str:
|
||||
"""计算文件的SHA-256哈希值"""
|
||||
return hashlib.sha256(file_content).hexdigest()
|
||||
|
||||
def upload_test_file_with_hash():
|
||||
"""上传测试文件并展示哈希计算过程"""
|
||||
|
||||
# 创建测试文件内容
|
||||
original_content = "Hello World! 这是演示文件哈希的测试内容。\n包含中文和English混合内容。"
|
||||
|
||||
print(f"📄 原始文件内容:")
|
||||
print(f"'{original_content}'")
|
||||
print(f"📏 文件大小: {len(original_content.encode('utf-8'))} bytes")
|
||||
print()
|
||||
|
||||
# 计算哈希值
|
||||
file_hash = calculate_sha256_hash(original_content.encode('utf-8'))
|
||||
print(f"🔒 计算SHA-256哈希值:")
|
||||
print(f"{file_hash}")
|
||||
print()
|
||||
|
||||
# 上传文件
|
||||
try:
|
||||
files = {
|
||||
"file": ("demo_hash_test.txt", original_content.encode('utf-8'), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"description": "演示文件哈希功能",
|
||||
"tags": "demo,hash,test",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
server_hash = file_info["file_hash"]
|
||||
file_id = file_info["id"]
|
||||
|
||||
print(f"✅ 文件上传成功!")
|
||||
print(f"📋 文件ID: {file_id}")
|
||||
print(f"📁 服务器存储的文件名: {file_info['filename']}")
|
||||
print(f"🔒 服务器计算的哈希值: {server_hash}")
|
||||
print()
|
||||
|
||||
# 验证哈希值一致性
|
||||
if file_hash == server_hash:
|
||||
print(f"✅ 哈希值验证通过! 客户端和服务器计算结果一致")
|
||||
else:
|
||||
print(f"❌ 哈希值验证失败! 客户端和服务器计算结果不一致")
|
||||
print(f" 客户端: {file_hash}")
|
||||
print(f" 服务器: {server_hash}")
|
||||
|
||||
return file_id, original_content, file_hash
|
||||
else:
|
||||
print(f"❌ 上传失败: {response.text}")
|
||||
return None, None, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 上传出错: {e}")
|
||||
return None, None, None
|
||||
|
||||
def download_and_verify_file(file_id: int, original_content: str, original_hash: str):
|
||||
"""下载文件并验证完整性"""
|
||||
|
||||
print(f"\n📥 开始下载和验证文件...")
|
||||
|
||||
try:
|
||||
# 下载文件
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/download",
|
||||
json=data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
downloaded_content = response.content.decode('utf-8')
|
||||
|
||||
print(f"📄 下载的文件内容:")
|
||||
print(f"'{downloaded_content}'")
|
||||
print()
|
||||
|
||||
# 验证内容完整性
|
||||
if downloaded_content == original_content:
|
||||
print(f"✅ 文件内容完整性验证通过!")
|
||||
else:
|
||||
print(f"❌ 文件内容完整性验证失败!")
|
||||
print(f" 原始内容: '{original_content}'")
|
||||
print(f" 下载内容: '{downloaded_content}'")
|
||||
|
||||
# 计算下载文件的哈希值
|
||||
downloaded_hash = calculate_sha256_hash(downloaded_content.encode('utf-8'))
|
||||
print(f"🔒 下载文件的哈希值:")
|
||||
print(f"{downloaded_hash}")
|
||||
print()
|
||||
|
||||
# 验证哈希值
|
||||
if downloaded_hash == original_hash:
|
||||
print(f"✅ 下载文件哈希验证通过! 文件完整性得到保证")
|
||||
else:
|
||||
print(f"❌ 下载文件哈希验证失败! 文件可能已损坏")
|
||||
print(f" 原始哈希: {original_hash}")
|
||||
print(f" 下载哈希: {downloaded_hash}")
|
||||
|
||||
else:
|
||||
print(f"❌ 下载失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 下载过程出错: {e}")
|
||||
|
||||
def demonstrate_file_duplication():
|
||||
"""演示文件去重功能"""
|
||||
|
||||
print(f"\n🔄 演示文件去重功能...")
|
||||
print(f"尝试上传相同内容的文件,系统应该拒绝重复上传...")
|
||||
|
||||
# 创建与之前相同内容的文件
|
||||
duplicate_content = "Hello World! 这是演示文件哈希的测试内容。\n包含中文和English混合内容。"
|
||||
|
||||
try:
|
||||
files = {
|
||||
"file": ("duplicate_file.txt", duplicate_content.encode('utf-8'), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"description": "重复文件测试",
|
||||
"tags": "duplicate,test",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
if response.status_code == 409: # 409 Conflict 表示文件已存在
|
||||
result = response.json()
|
||||
print(f"✅ 文件去重功能正常工作!")
|
||||
print(f"📋 系统检测到文件已存在,拒绝重复上传")
|
||||
print(f"📄 原始文件名: {result.get('detail', {}).get('filename', 'Unknown')}")
|
||||
elif response.status_code == 201:
|
||||
print(f"⚠️ 文件去重功能可能未正常工作,重复上传成功了")
|
||||
else:
|
||||
print(f"❌ 测试失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 测试过程出错: {e}")
|
||||
|
||||
def show_file_location_on_server():
|
||||
"""显示文件在服务器上的存储位置"""
|
||||
|
||||
print(f"\n📁 文件在服务器上的存储位置:")
|
||||
print(f"后端上传目录: backend/uploads/")
|
||||
|
||||
# 列出uploads目录中的文件
|
||||
try:
|
||||
if os.path.exists("backend/uploads"):
|
||||
files = os.listdir("backend/uploads")
|
||||
if files:
|
||||
print(f"当前存储的文件:")
|
||||
for file in files:
|
||||
file_path = os.path.join("backend/uploads", file)
|
||||
file_size = os.path.getsize(file_path)
|
||||
print(f" 📄 {file} ({file_size} bytes)")
|
||||
|
||||
# 计算并显示文件的哈希值
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read()
|
||||
file_hash = calculate_sha256_hash(content)
|
||||
print(f" 🔒 SHA-256: {file_hash}")
|
||||
else:
|
||||
print(f" (目录为空)")
|
||||
else:
|
||||
print(f" (uploads目录不存在)")
|
||||
except Exception as e:
|
||||
print(f" 无法读取目录: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🔐 文件哈希原理演示")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. 上传测试文件
|
||||
file_id, original_content, original_hash = upload_test_file_with_hash()
|
||||
|
||||
if file_id:
|
||||
# 2. 下载并验证文件
|
||||
download_and_verify_file(file_id, original_content, original_hash)
|
||||
|
||||
# 3. 演示文件去重
|
||||
demonstrate_file_duplication()
|
||||
|
||||
# 4. 显示文件存储位置
|
||||
show_file_location_on_server()
|
||||
|
||||
print(f"\n🎉 演示完成!")
|
||||
print(f"📚 关键知识点:")
|
||||
print(f" • SHA-256哈希值用于验证文件完整性")
|
||||
print(f" • 相同内容的文件具有相同的哈希值")
|
||||
print(f" • 系统通过哈希值检测重复文件")
|
||||
print(f" • 文件在上传、存储、下载过程中保持完整性")
|
||||
else:
|
||||
print(f"\n❌ 演示失败,无法上传测试文件")
|
||||
@@ -1,236 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 专门解决docs无法访问问题的脚本
|
||||
|
||||
import subprocess
|
||||
import socket
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def check_fastapi_docs():
|
||||
"""检查FastAPI docs相关的常见问题"""
|
||||
print("🔍 FastAPI Docs 诊断工具")
|
||||
print("=" * 50)
|
||||
|
||||
# 1. 检查FastAPI版本
|
||||
try:
|
||||
import fastapi
|
||||
print(f"✓ FastAPI版本: {fastapi.__version__}")
|
||||
except ImportError:
|
||||
print("❌ FastAPI未安装")
|
||||
return False
|
||||
|
||||
# 2. 检查uvicorn版本
|
||||
try:
|
||||
import uvicorn
|
||||
print(f"✓ Uvicorn版本: {uvicorn.__version__}")
|
||||
except ImportError:
|
||||
print("❌ Uvicorn未安装")
|
||||
return False
|
||||
|
||||
# 3. 检查端口占用
|
||||
print("\n📡 网络诊断:")
|
||||
for port in [8000, 8001, 8002]:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(1)
|
||||
result = sock.connect_ex(('127.0.0.1', port))
|
||||
if result == 0:
|
||||
print(f"❌ 端口 {port} 被占用")
|
||||
else:
|
||||
print(f"✓ 端口 {port} 可用")
|
||||
sock.close()
|
||||
|
||||
# 4. 获取本机IP
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
print(f"✓ 本机IP: {local_ip}")
|
||||
except:
|
||||
local_ip = "127.0.0.1"
|
||||
print(f"⚠️ 使用回环地址: {local_ip}")
|
||||
|
||||
# 5. 测试不同的host配置
|
||||
print("\n🧪 测试不同配置:")
|
||||
|
||||
test_configs = [
|
||||
("127.0.0.1", "仅本地访问"),
|
||||
("0.0.0.0", "允许外部访问"),
|
||||
("localhost", "主机名访问")
|
||||
]
|
||||
|
||||
for host, desc in test_configs:
|
||||
print(f" {host} - {desc}")
|
||||
|
||||
return True, local_ip
|
||||
|
||||
def create_working_server():
|
||||
"""创建可以正常访问docs的服务器"""
|
||||
print("\n🔧 创建可用的服务器配置...")
|
||||
|
||||
server_content = '''#!/usr/bin/env python3
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
import socket
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc",
|
||||
openapi_url="/openapi.json"
|
||||
)
|
||||
|
||||
# 确保CORS配置正确
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"redoc": "/redoc",
|
||||
"openapi": "/openapi.json"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"docs_available": True
|
||||
}
|
||||
|
||||
@app.get("/test-docs")
|
||||
async def test_docs():
|
||||
"""测试docs是否可用"""
|
||||
return {
|
||||
"docs_url": "/docs",
|
||||
"redoc_url": "/redoc",
|
||||
"openapi_url": "/openapi.json",
|
||||
"message": "如果看到这个页面,说明服务正常运行,请尝试访问 /docs"
|
||||
}
|
||||
|
||||
def get_available_port():
|
||||
"""获取可用端口"""
|
||||
for port in range(8000, 8010):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('0.0.0.0', port))
|
||||
return port
|
||||
except OSError:
|
||||
continue
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = get_available_port()
|
||||
if port is None:
|
||||
print("❌ 无法找到可用端口")
|
||||
sys.exit(1)
|
||||
|
||||
# 获取本机IP
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
local_ip = "127.0.0.1"
|
||||
|
||||
print(f"🚀 启动服务在端口 {port}")
|
||||
print("=" * 60)
|
||||
print(f"📍 本地访问:")
|
||||
print(f" 根路径: http://localhost:{port}")
|
||||
print(f" API文档: http://localhost:{port}/docs")
|
||||
print(f" ReDoc: http://localhost:{port}/redoc")
|
||||
print(f" 测试页面: http://localhost:{port}/test-docs")
|
||||
print("")
|
||||
print(f"📍 网络访问:")
|
||||
print(f" 根路径: http://{local_ip}:{port}")
|
||||
print(f" API文档: http://{local_ip}:{port}/docs")
|
||||
print(f" ReDoc: http://{local_ip}:{port}/redoc")
|
||||
print("=" * 60)
|
||||
print("💡 如果无法访问,请检查:")
|
||||
print(" 1. 防火墙设置")
|
||||
print(" 2. 网络连接")
|
||||
print(" 3. 浏览器是否阻止访问")
|
||||
print(" 4. 尝试不同的浏览器")
|
||||
print("⏹️ 按 Ctrl+C 停止服务")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=port,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("\\n服务已停止")
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
'''
|
||||
|
||||
with open('working_server.py', 'w', encoding='utf-8') as f:
|
||||
f.write(server_content)
|
||||
|
||||
print("✓ 已创建 working_server.py")
|
||||
return True
|
||||
|
||||
def test_curl_commands():
|
||||
"""提供curl测试命令"""
|
||||
print("\n🌐 提供测试命令:")
|
||||
|
||||
print("\\n1. 测试根路径:")
|
||||
print("curl http://localhost:8000")
|
||||
|
||||
print("\\n2. 测试健康检查:")
|
||||
print("curl http://localhost:8000/health")
|
||||
|
||||
print("\\n3. 测试API文档端点:")
|
||||
print("curl http://localhost:8000/docs")
|
||||
|
||||
print("\\n4. 测试OpenAPI JSON:")
|
||||
print("curl http://localhost:8000/openapi.json")
|
||||
|
||||
print("\\n5. 测试ReDoc:")
|
||||
print("curl http://localhost:8000/redoc")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 检查环境
|
||||
success, local_ip = check_fastapi_docs()
|
||||
|
||||
if not success:
|
||||
print("\\n❌ 环境检查失败,请安装必要的依赖")
|
||||
print("pip install fastapi uvicorn")
|
||||
return
|
||||
|
||||
# 创建可用服务器
|
||||
create_working_server()
|
||||
|
||||
# 提供测试命令
|
||||
test_curl_commands()
|
||||
|
||||
print("\\n" + "=" * 60)
|
||||
print("🎯 解决方案:")
|
||||
print("1. 运行: python working_server.py")
|
||||
print("2. 在浏览器中访问显示的URL")
|
||||
print("3. 如果仍然无法访问,请检查:")
|
||||
print(" - 防火墙设置")
|
||||
print(" - 浏览器阻止")
|
||||
print(" - 网络代理设置")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,118 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# CORS跨域问题快速修复脚本
|
||||
|
||||
echo "=== CORS跨域问题修复工具 ==="
|
||||
|
||||
# 检查当前目录
|
||||
if [ ! -f "main.py" ]; then
|
||||
echo "错误: 请在包含main.py的项目根目录下运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "当前目录: $(pwd)"
|
||||
|
||||
# 1. 更新配置文件
|
||||
echo ""
|
||||
echo "1. 更新CORS配置..."
|
||||
|
||||
# 更新config.py
|
||||
echo "更新 app/core/config.py..."
|
||||
sed -i 's/ALLOWED_HOSTS: List\[str\] = \[.*\]/ALLOWED_HOSTS: List[str] = ["*"] # 允许所有域名访问/' app/core/config.py
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ config.py 更新成功"
|
||||
else
|
||||
echo "✗ config.py 更新失败,请手动检查"
|
||||
fi
|
||||
|
||||
# 2. 更新.env文件
|
||||
echo ""
|
||||
echo "2. 更新环境配置..."
|
||||
if [ -f ".env" ]; then
|
||||
# 备份原始文件
|
||||
cp .env .env.backup.$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 更新CORS配置
|
||||
sed -i 's/ALLOWED_HOSTS=\[.*\]/ALLOWED_HOSTS=["*"]/' .env
|
||||
|
||||
if grep -q 'ALLOWED_HOSTS=\["\*"\]' .env; then
|
||||
echo "✓ .env 文件更新成功"
|
||||
else
|
||||
echo "✗ .env 文件更新失败,请手动检查"
|
||||
fi
|
||||
else
|
||||
echo ".env 文件不存在,创建新的配置..."
|
||||
cat > .env << EOF
|
||||
# 基础配置
|
||||
ENVIRONMENT=production
|
||||
DEBUG=false
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_URL=mysql+pymysql://用户名:密码@localhost:3306/数据库名
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production-$(date +%s)
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRE_MINUTES=30
|
||||
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
|
||||
# CORS配置
|
||||
ALLOWED_HOSTS=["*"]
|
||||
EOF
|
||||
echo "✓ .env 文件创建成功"
|
||||
fi
|
||||
|
||||
# 3. 验证配置
|
||||
echo ""
|
||||
echo "3. 验证CORS配置..."
|
||||
|
||||
# 检查config.py
|
||||
if grep -q 'ALLOWED_HOSTS: List\[str\] = \["\*"\]' app/core/config.py; then
|
||||
echo "✓ config.py CORS配置正确"
|
||||
else
|
||||
echo "✗ config.py CORS配置可能有问题"
|
||||
fi
|
||||
|
||||
# 检查.env文件
|
||||
if grep -q 'ALLOWED_HOSTS=\["\*"\]' .env; then
|
||||
echo "✓ .env CORS配置正确"
|
||||
else
|
||||
echo "✗ .env CORS配置可能有问题"
|
||||
fi
|
||||
|
||||
# 4. 重启服务提示
|
||||
echo ""
|
||||
echo "4. 重启服务..."
|
||||
echo "配置更新完成,请重启应用以使配置生效"
|
||||
echo ""
|
||||
echo "重启方式:"
|
||||
echo "1. 如果应用正在运行,请按 Ctrl+C 停止"
|
||||
echo "2. 然后重新启动: python main.py"
|
||||
echo "3. 或者使用启动脚本: ./start_app.sh"
|
||||
echo ""
|
||||
echo "如果使用Docker部署:"
|
||||
echo "1. 重新构建镜像: docker build -t cloud-drive-backend:latest ."
|
||||
echo "2. 重新运行容器: docker run -d -p 8002:8002 cloud-drive-backend:latest"
|
||||
|
||||
# 5. 测试CORS
|
||||
echo ""
|
||||
echo "5. CORS测试建议..."
|
||||
echo "重启后,可以通过以下方式测试CORS:"
|
||||
echo "1. 浏览器开发者工具 -> Network -> 查看请求头"
|
||||
echo "2. 检查是否有 'Access-Control-Allow-Origin: *' 头"
|
||||
echo "3. 使用curl测试: curl -H 'Origin: http://example.com' -H 'Access-Control-Request-Method: POST' -H 'Access-Control-Request-Headers: X-Requested-With' -X OPTIONS http://localhost:8002/api/v1/health"
|
||||
|
||||
echo ""
|
||||
echo "=== CORS修复完成 ==="
|
||||
echo ""
|
||||
echo "注意事项:"
|
||||
echo "- 允许所有域名访问 (\"*\") 仅适用于开发和测试环境"
|
||||
echo "- 生产环境建议设置具体的允许域名列表"
|
||||
echo "- 如需更安全的CORS配置,请手动修改 ALLOWED_HOSTS"
|
||||
@@ -1,194 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 数据库连接问题修复脚本
|
||||
|
||||
echo "=== 数据库连接问题修复工具 ==="
|
||||
|
||||
# 检查当前目录
|
||||
if [ ! -f "main.py" ]; then
|
||||
echo "错误: 请在包含main.py的项目根目录下运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "当前目录: $(pwd)"
|
||||
|
||||
# 1. 检查.env文件
|
||||
echo ""
|
||||
echo "1. 检查环境配置..."
|
||||
|
||||
if [ -f ".env" ]; then
|
||||
echo "✓ .env 文件存在"
|
||||
echo "当前数据库配置:"
|
||||
grep "DATABASE_URL" .env || echo "DATABASE_URL 未设置"
|
||||
else
|
||||
echo "⚠ .env 文件不存在,正在创建..."
|
||||
cat > .env << EOF
|
||||
# 基础配置
|
||||
ENVIRONMENT=production
|
||||
DEBUG=false
|
||||
|
||||
# 数据库配置 - 请根据实际情况修改
|
||||
DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production-$(date +%s)
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRE_MINUTES=30
|
||||
|
||||
# 文件上传配置
|
||||
UPLOAD_DIR=uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
|
||||
# CORS配置
|
||||
ALLOWED_HOSTS=["*"]
|
||||
EOF
|
||||
echo "✓ .env 文件创建成功"
|
||||
fi
|
||||
|
||||
# 2. 测试数据库连接
|
||||
echo ""
|
||||
echo "2. 测试数据库连接..."
|
||||
|
||||
# 创建测试脚本
|
||||
cat > test_db_connection.py << 'EOF'
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
try:
|
||||
from app.core.config import settings
|
||||
print(f"✓ 配置加载成功")
|
||||
print(f"数据库URL: {settings.DATABASE_URL}")
|
||||
|
||||
# 测试数据库连接
|
||||
from sqlalchemy import create_engine, text
|
||||
engine = create_engine(settings.DATABASE_URL)
|
||||
|
||||
with engine.connect() as conn:
|
||||
result = conn.execute(text("SELECT VERSION()"))
|
||||
version = result.fetchone()[0]
|
||||
print(f"✓ 数据库连接成功: MySQL {version}")
|
||||
|
||||
# 检查数据库是否存在
|
||||
result = conn.execute(text("SHOW DATABASES LIKE 'mytest_db'"))
|
||||
if result.fetchone():
|
||||
print("✓ 数据库 'mytest_db' 存在")
|
||||
else:
|
||||
print("⚠ 数据库 'mytest_db' 不存在,需要创建")
|
||||
|
||||
except ImportError as e:
|
||||
print(f"✗ 导入错误: {e}")
|
||||
print("请确保已安装所需依赖: pip install sqlalchemy pymysql")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"✗ 数据库连接失败: {e}")
|
||||
print("")
|
||||
print "可能的原因:"
|
||||
print "1. 数据库服务器未启动"
|
||||
print "2. 网络连接问题"
|
||||
print "3. 用户名或密码错误"
|
||||
print "4. 数据库不存在"
|
||||
print "5. 防火墙阻止连接"
|
||||
sys.exit(1)
|
||||
EOF
|
||||
|
||||
python3 test_db_connection.py
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "✓ 数据库连接测试通过"
|
||||
else
|
||||
echo ""
|
||||
echo "✗ 数据库连接测试失败"
|
||||
echo ""
|
||||
echo "解决方案:"
|
||||
echo "1. 检查数据库服务是否运行"
|
||||
echo "2. 验证数据库连接参数"
|
||||
echo "3. 确认网络连通性"
|
||||
echo ""
|
||||
echo "请手动编辑 .env 文件中的 DATABASE_URL"
|
||||
echo "格式: mysql+pymysql://用户名:密码@主机:端口/数据库名"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 3. 检查Docker环境
|
||||
echo ""
|
||||
echo "3. 检查Docker配置..."
|
||||
|
||||
if [ -f "docker-compose.yml" ]; then
|
||||
echo "✓ 发现 docker-compose.yml 文件"
|
||||
echo "检查Docker数据库配置..."
|
||||
|
||||
if grep -q "mysql:" docker-compose.yml; then
|
||||
echo "⚠ 检测到Docker MySQL配置"
|
||||
echo "如果使用Docker Compose,请确保:"
|
||||
echo "1. 数据库服务已启动: docker-compose up -d mysql"
|
||||
echo "2. 数据库主机名应为 'mysql' (服务名)"
|
||||
echo "3. 确认网络配置正确"
|
||||
|
||||
echo ""
|
||||
echo "Docker数据库连接配置示例:"
|
||||
echo "DATABASE_URL=mysql+pymysql://root:password@mysql:3306/mytest_db"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. 提供修复建议
|
||||
echo ""
|
||||
echo "4. 修复建议..."
|
||||
|
||||
echo "根据错误信息,应用尝试连接到 'mysql' 主机,但配置中是IP地址。"
|
||||
echo "请检查以下配置:"
|
||||
echo ""
|
||||
|
||||
echo "选项1: 使用外部数据库 (推荐)"
|
||||
echo "DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db"
|
||||
echo ""
|
||||
|
||||
echo "选项2: 使用Docker数据库"
|
||||
echo "DATABASE_URL=mysql+pymysql://root:password@mysql:3306/mytest_db"
|
||||
echo ""
|
||||
|
||||
echo "选项3: 使用本地数据库"
|
||||
echo "DATABASE_URL=mysql+pymysql://root:password@localhost:3306/mytest_db"
|
||||
echo ""
|
||||
|
||||
# 5. 自动修复.env文件
|
||||
echo "5. 自动修复配置..."
|
||||
if [ -f ".env" ]; then
|
||||
# 备份原文件
|
||||
cp .env .env.backup.$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 确保使用正确的数据库URL
|
||||
if grep -q "DATABASE_URL.*mysql.*mysql:" .env; then
|
||||
echo "检测到Docker主机名配置,更新为外部数据库..."
|
||||
sed -i 's|DATABASE_URL=mysql+pymysql://.*@mysql:.*|DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db|' .env
|
||||
elif ! grep -q "DATABASE_URL.*101.126.85.76" .env; then
|
||||
echo "更新数据库连接配置..."
|
||||
sed -i 's|DATABASE_URL=.*|DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db|' .env
|
||||
fi
|
||||
|
||||
echo "✓ 数据库配置已更新"
|
||||
fi
|
||||
|
||||
# 6. 重启应用提示
|
||||
echo ""
|
||||
echo "6. 重启应用..."
|
||||
echo "配置更新完成,请重启应用以使配置生效"
|
||||
echo ""
|
||||
echo "重启方式:"
|
||||
echo "1. 停止当前应用 (Ctrl+C)"
|
||||
echo "2. 重新启动: python main.py"
|
||||
echo "3. 或者使用启动脚本: ./start_app.sh"
|
||||
|
||||
echo ""
|
||||
echo "=== 数据库连接修复完成 ==="
|
||||
echo ""
|
||||
echo "如果问题仍然存在,请:"
|
||||
echo "1. 确认数据库服务器地址正确: 101.126.85.76:3306"
|
||||
echo "2. 确认用户名密码正确: mytest_db / mytest_db"
|
||||
echo "3. 确认数据库名称正确: mytest_db"
|
||||
echo "4. 测试网络连通性: telnet 101.126.85.76 3306"
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 依赖修复脚本 - 解决email-validator缺失问题
|
||||
|
||||
echo "=== 云盘后端依赖修复工具 ==="
|
||||
|
||||
# 检查当前用户
|
||||
echo "当前用户: $(whoami)"
|
||||
echo "用户ID: $EUID"
|
||||
|
||||
# 1. 检查Python环境
|
||||
echo ""
|
||||
echo "1. 检查Python环境..."
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "错误: 未找到python3"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Python版本: $(python3 --version)"
|
||||
|
||||
# 2. 检查pip
|
||||
echo ""
|
||||
echo "2. 检查pip..."
|
||||
if ! command -v pip3 &> /dev/null; then
|
||||
echo "错误: 未找到pip3"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ pip版本: $(pip3 --version)"
|
||||
|
||||
# 3. 升级pip
|
||||
echo ""
|
||||
echo "3. 升级pip..."
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
pip3 install --upgrade pip
|
||||
else
|
||||
pip3 install --user --upgrade pip
|
||||
fi
|
||||
echo "✓ pip升级完成"
|
||||
|
||||
# 4. 安装email-validator(单独安装确保成功)
|
||||
echo ""
|
||||
echo "4. 安装email-validator..."
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
pip3 install email-validator
|
||||
else
|
||||
pip3 install --user email-validator
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ email-validator安装成功"
|
||||
else
|
||||
echo "✗ email-validator安装失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 5. 验证email-validator安装
|
||||
echo ""
|
||||
echo "5. 验证email-validator安装..."
|
||||
python3 -c "import email_validator; print('✓ email-validator导入成功')" || {
|
||||
echo "✗ email-validator验证失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 6. 安装其他核心依赖
|
||||
echo ""
|
||||
echo "6. 安装其他核心依赖..."
|
||||
CORE_PACKAGES="fastapi uvicorn sqlalchemy pymysql redis python-jose passlib python-multipart pydantic pydantic-settings httpx python-dotenv alembic bcrypt"
|
||||
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
pip3 install $CORE_PACKAGES
|
||||
else
|
||||
pip3 install --user $CORE_PACKAGES
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ 核心依赖安装成功"
|
||||
else
|
||||
echo "✗ 核心依赖安装失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 7. 验证核心包导入
|
||||
echo ""
|
||||
echo "7. 验证核心包导入..."
|
||||
python3 -c "
|
||||
try:
|
||||
import fastapi, uvicorn, sqlalchemy, pymysql, redis, jose, passlib, pydantic, httpx, alembic
|
||||
print('✓ 所有核心包导入成功')
|
||||
except ImportError as e:
|
||||
print(f'✗ 包导入失败: {e}')
|
||||
exit(1)
|
||||
" || exit 1
|
||||
|
||||
# 8. 测试Pydantic配置
|
||||
echo ""
|
||||
echo "8. 测试Pydantic配置..."
|
||||
python3 -c "
|
||||
try:
|
||||
from pydantic import BaseModel, EmailStr
|
||||
print('✓ Pydantic EmailStr类型可用')
|
||||
except Exception as e:
|
||||
print(f'✗ Pydantic EmailStr测试失败: {e}')
|
||||
print('尝试重新安装pydantic[email]...')
|
||||
exit(1)
|
||||
" || {
|
||||
echo "重新安装pydantic[email]..."
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
pip3 install "pydantic[email]"
|
||||
else
|
||||
pip3 install --user "pydantic[email]"
|
||||
fi
|
||||
}
|
||||
|
||||
# 9. 测试应用导入
|
||||
echo ""
|
||||
echo "9. 测试应用核心模块导入..."
|
||||
python3 -c "
|
||||
import sys
|
||||
sys.path.insert(0, '.')
|
||||
try:
|
||||
from app.core.config import settings
|
||||
print('✓ 应用配置模块导入成功')
|
||||
except Exception as e:
|
||||
print(f'✗ 应用配置导入失败: {e}')
|
||||
print('可能需要检查应用代码')
|
||||
exit(1)
|
||||
" || {
|
||||
echo "应用导入测试失败,但依赖已安装"
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "=== 依赖修复完成 ==="
|
||||
echo ""
|
||||
echo "已成功安装的包:"
|
||||
echo "- email-validator (邮件验证)"
|
||||
echo "- fastapi (Web框架)"
|
||||
echo "- uvicorn (ASGI服务器)"
|
||||
echo "- sqlalchemy (ORM)"
|
||||
echo "- pymysql (MySQL驱动)"
|
||||
echo "- redis (Redis客户端)"
|
||||
echo "- python-jose (JWT处理)"
|
||||
echo "- passlib (密码处理)"
|
||||
echo "- pydantic (数据验证)"
|
||||
echo "- httpx (HTTP客户端)"
|
||||
echo "- alembic (数据库迁移)"
|
||||
echo "- bcrypt (密码哈希)"
|
||||
echo ""
|
||||
echo "现在可以运行应用:"
|
||||
echo "python3 main.py"
|
||||
echo ""
|
||||
echo "或使用部署脚本:"
|
||||
echo "./quick_deploy_linux.sh"
|
||||
@@ -1,264 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 修复模块导入问题的脚本
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def check_project_structure():
|
||||
"""检查项目结构"""
|
||||
print("=== 检查项目结构 ===")
|
||||
|
||||
current_dir = Path.cwd()
|
||||
print(f"当前目录: {current_dir}")
|
||||
|
||||
# 检查app目录
|
||||
app_dir = current_dir / 'app'
|
||||
if app_dir.exists():
|
||||
print("✓ app目录存在")
|
||||
else:
|
||||
print("✗ app目录不存在")
|
||||
return False
|
||||
|
||||
# 检查app/core目录
|
||||
core_dir = app_dir / 'core'
|
||||
if core_dir.exists():
|
||||
print("✓ app/core目录存在")
|
||||
else:
|
||||
print("✗ app/core目录不存在")
|
||||
return False
|
||||
|
||||
# 检查关键文件
|
||||
key_files = [
|
||||
'app/__init__.py',
|
||||
'app/core/__init__.py',
|
||||
'app/core/config.py',
|
||||
'main.py'
|
||||
]
|
||||
|
||||
for file_path in key_files:
|
||||
if (current_dir / file_path).exists():
|
||||
print(f"✓ {file_path} 存在")
|
||||
else:
|
||||
print(f"✗ {file_path} 不存在")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def create_missing_files():
|
||||
"""创建缺失的文件"""
|
||||
print("\n=== 创建缺失文件 ===")
|
||||
|
||||
current_dir = Path.cwd()
|
||||
|
||||
# 创建app/__init__.py
|
||||
app_init = current_dir / 'app' / '__init__.py'
|
||||
if not app_init.exists():
|
||||
with open(app_init, 'w') as f:
|
||||
f.write('"""云盘应用包"""\n')
|
||||
print("✓ 创建 app/__init__.py")
|
||||
|
||||
# 创建app/core/__init__.py
|
||||
core_init = current_dir / 'app' / 'core' / '__init__.py'
|
||||
if not core_init.exists():
|
||||
with open(core_init, 'w') as f:
|
||||
f.write('"""核心模块包"""\n')
|
||||
print("✓ 创建 app/core/__init__.py")
|
||||
|
||||
# 创建app/core/config.py(如果不存在)
|
||||
config_file = current_dir / 'app' / 'core' / 'config.py'
|
||||
if not config_file.exists():
|
||||
config_content = '''from pydantic_settings import BaseSettings
|
||||
from typing import List
|
||||
import os
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# 基础配置
|
||||
ENVIRONMENT: str = "development"
|
||||
DEBUG: bool = True
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_URL: str = "mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db"
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL: str = "redis://localhost:6379"
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY: str = "your-super-secret-jwt-key-change-in-production"
|
||||
JWT_ALGORITHM: str = "HS256"
|
||||
JWT_EXPIRE_MINUTES: int = 30
|
||||
JWT_REFRESH_EXPIRE_DAYS: int = 7
|
||||
|
||||
# CORS配置
|
||||
ALLOWED_HOSTS: List[str] = [
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3001",
|
||||
"http://localhost:3002",
|
||||
"http://localhost:3003",
|
||||
"http://localhost:3004",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://127.0.0.1:3001",
|
||||
"http://127.0.0.1:3002",
|
||||
"http://127.0.0.1:3003",
|
||||
"http://127.0.0.1:3004",
|
||||
"http://172.16.16.89:3000",
|
||||
"http://172.16.16.89:3001",
|
||||
"http://172.16.16.89:3002",
|
||||
"http://172.16.16.89:3003",
|
||||
"http://172.16.16.89:3004",
|
||||
"*"
|
||||
]
|
||||
|
||||
# 文件上传配置
|
||||
MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 10MB
|
||||
UPLOAD_DIR: str = "uploads"
|
||||
ALLOWED_EXTENSIONS: List[str] = [
|
||||
# 图片
|
||||
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg",
|
||||
# 文档
|
||||
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
|
||||
".txt", ".rtf", ".csv",
|
||||
# 压缩文件
|
||||
".zip", ".rar", ".7z", ".tar", ".gz",
|
||||
# 音频
|
||||
".mp3", ".wav", ".flac", ".aac", ".ogg",
|
||||
# 视频
|
||||
".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv",
|
||||
# 代码文件
|
||||
".py", ".js", ".html", ".css", ".json", ".xml", ".yaml", ".yml",
|
||||
".java", ".cpp", ".c", ".h", ".cs", ".php", ".rb", ".go",
|
||||
".sql", ".sh", ".bat", ".ps1", ".md", ".log"
|
||||
]
|
||||
|
||||
# 安全配置
|
||||
BCRYPT_ROUNDS: int = 12
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
case_sensitive = True
|
||||
|
||||
settings = Settings()
|
||||
'''
|
||||
with open(config_file, 'w') as f:
|
||||
f.write(config_content)
|
||||
print("✓ 创建 app/core/config.py")
|
||||
|
||||
def create_fixed_main_py():
|
||||
"""创建修复版main.py"""
|
||||
current_dir = Path.cwd()
|
||||
main_file = current_dir / 'main.py'
|
||||
|
||||
fixed_content = '''#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
云盘后端应用主入口
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 确保项目根目录在Python路径中
|
||||
current_dir = Path(__file__).parent
|
||||
if str(current_dir) not in sys.path:
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
try:
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
import uvicorn
|
||||
from datetime import datetime
|
||||
|
||||
# 简单的日志打印函数
|
||||
def log_info(message):
|
||||
"""打印INFO级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] INFO: {message}")
|
||||
|
||||
def log_error(message):
|
||||
"""打印ERROR级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] ERROR: {message}")
|
||||
|
||||
def log_debug(message):
|
||||
"""打印DEBUG级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] DEBUG: {message}")
|
||||
|
||||
# 确保logs目录存在
|
||||
logs_dir = current_dir / "logs"
|
||||
logs_dir.mkdir(exist_ok=True)
|
||||
|
||||
log_info("=== Server Starting ===")
|
||||
log_info(f"Python version: {sys.version}")
|
||||
log_info(f"Working directory: {os.getcwd()}")
|
||||
log_info("Simple print logger configured")
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "云盘应用 API", "version": "1.0.1"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(
|
||||
"main:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=True if settings.ENVIRONMENT == "development" else False
|
||||
)
|
||||
|
||||
except ImportError as e:
|
||||
print(f"导入错误: {e}")
|
||||
print("请确保已安装所有依赖: pip install -r requirements.txt")
|
||||
print("或尝试安装基础依赖: pip install fastapi uvicorn sqlalchemy pymysql redis python-jose passlib python-multipart pydantic pydantic-settings httpx python-dotenv")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"启动错误: {e}")
|
||||
sys.exit(1)
|
||||
'''
|
||||
|
||||
with open(main_file, 'w') as f:
|
||||
f.write(fixed_content)
|
||||
print("✓ 创建修复版 main.py")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 修复模块导入问题 ===")
|
||||
|
||||
# 检查项目结构
|
||||
if not check_project_structure():
|
||||
print("\\n项目结构有问题,开始修复...")
|
||||
create_missing_files()
|
||||
create_fixed_main_py()
|
||||
|
||||
print("\\n=== 修复完成 ===")
|
||||
print("现在可以运行:")
|
||||
print("1. 激活虚拟环境: source venv/bin/activate")
|
||||
print("2. 安装依赖: pip install -r requirements.txt")
|
||||
print("3. 启动服务: python main.py")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,155 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 权限问题修复脚本
|
||||
|
||||
echo "=== 云盘后端权限修复工具 ==="
|
||||
|
||||
# 检查当前用户
|
||||
echo "当前用户: $(whoami)"
|
||||
echo "用户ID: $EUID"
|
||||
echo "主目录: $HOME"
|
||||
echo "当前目录: $(pwd)"
|
||||
|
||||
# 1. 检查和创建.config目录
|
||||
echo ""
|
||||
echo "1. 检查用户配置目录..."
|
||||
if [ ! -d "$HOME/.config" ]; then
|
||||
echo "创建 .config 目录..."
|
||||
mkdir -p "$HOME/.config" || {
|
||||
echo "错误: 无法创建 .config 目录"
|
||||
exit 1
|
||||
}
|
||||
echo "✓ .config 目录创建成功"
|
||||
else
|
||||
echo "✓ .config 目录已存在"
|
||||
fi
|
||||
|
||||
# 2. 检查和创建systemd用户目录
|
||||
echo ""
|
||||
echo "2. 检查systemd用户目录..."
|
||||
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
|
||||
|
||||
if [ ! -d "$SYSTEMD_USER_DIR" ]; then
|
||||
echo "创建systemd用户目录..."
|
||||
mkdir -p "$SYSTEMD_USER_DIR" || {
|
||||
echo "错误: 无法创建systemd用户目录"
|
||||
echo "尝试使用sudo创建..."
|
||||
sudo -u $(whoami) mkdir -p "$SYSTEMD_USER_DIR" || {
|
||||
echo "错误: 无法创建systemd用户目录,尝试使用临时目录"
|
||||
SYSTEMD_USER_DIR="/tmp/$(whoami)-systemd"
|
||||
mkdir -p "$SYSTEMD_USER_DIR"
|
||||
echo "使用临时目录: $SYSTEMD_USER_DIR"
|
||||
}
|
||||
}
|
||||
echo "✓ systemd用户目录创建成功: $SYSTEMD_USER_DIR"
|
||||
else
|
||||
echo "✓ systemd用户目录已存在: $SYSTEMD_USER_DIR"
|
||||
fi
|
||||
|
||||
# 3. 检查目录权限
|
||||
echo ""
|
||||
echo "3. 检查目录权限..."
|
||||
echo ".config 目录权限: $(ls -ld $HOME/.config | awk '{print $1,$3,$4}')"
|
||||
echo "systemd用户目录权限: $(ls -ld $SYSTEMD_USER_DIR | awk '{print $1,$3,$4}')"
|
||||
|
||||
# 4. 测试写入权限
|
||||
echo ""
|
||||
echo "4. 测试写入权限..."
|
||||
TEST_FILE="$SYSTEMD_USER_DIR/.test_write"
|
||||
if touch "$TEST_FILE" 2>/dev/null; then
|
||||
echo "✓ 写入权限正常"
|
||||
rm -f "$TEST_FILE"
|
||||
else
|
||||
echo "✗ 写入权限不足,尝试修复..."
|
||||
chmod 755 "$HOME/.config" 2>/dev/null
|
||||
chmod 755 "$SYSTEMD_USER_DIR" 2>/dev/null
|
||||
|
||||
if touch "$TEST_FILE" 2>/dev/null; then
|
||||
echo "✓ 权限修复成功"
|
||||
rm -f "$TEST_FILE"
|
||||
else
|
||||
echo "✗ 权限修复失败"
|
||||
echo "可能的解决方案:"
|
||||
echo "1. 检查磁盘空间: df -h"
|
||||
echo "2. 检查用户配额: quota -u $(whoami)"
|
||||
echo "3. 检查文件系统权限: mount | grep home"
|
||||
echo "4. 联系系统管理员"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 5. 检查systemd用户服务是否启用
|
||||
echo ""
|
||||
echo "5. 检查systemd用户服务..."
|
||||
if systemctl --user list-units --type=service --state=running &>/dev/null; then
|
||||
echo "✓ systemd用户服务可用"
|
||||
else
|
||||
echo "⚠ systemd用户服务可能不可用"
|
||||
echo "尝试启用用户systemd服务..."
|
||||
|
||||
# 对于某些系统,需要启用linger
|
||||
if command -v loginctl &> /dev/null; then
|
||||
if loginctl show-user $(whoami) | grep -q "Linger=no"; then
|
||||
echo "启用用户linger服务..."
|
||||
loginctl enable-linger $(whoami) || echo "无法启用linger,可能需要管理员权限"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 6. 创建测试服务文件
|
||||
echo ""
|
||||
echo "6. 创建测试服务文件..."
|
||||
SERVICE_FILE="$SYSTEMD_USER_DIR/cloud-drive.service"
|
||||
|
||||
cat > "$SERVICE_FILE" << EOF
|
||||
[Unit]
|
||||
Description=Cloud Drive Backend Service
|
||||
Documentation=https://github.com/your-repo/cloud-drive
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=$(pwd)
|
||||
ExecStart=$(pwd)/venv/bin/python $(pwd)/main.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cloud-drive
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
if [ -f "$SERVICE_FILE" ]; then
|
||||
echo "✓ 服务文件创建成功: $SERVICE_FILE"
|
||||
echo "文件权限: $(ls -l $SERVICE_FILE | awk '{print $1,$3,$4}')"
|
||||
else
|
||||
echo "✗ 服务文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 7. 测试systemd命令
|
||||
echo ""
|
||||
echo "7. 测试systemd命令..."
|
||||
if systemctl --user daemon-reload 2>/dev/null; then
|
||||
echo "✓ systemd用户服务重载成功"
|
||||
else
|
||||
echo "⚠ systemd用户服务重载失败"
|
||||
echo "错误信息: $(systemctl --user daemon-reload 2>&1)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 权限修复完成 ==="
|
||||
echo ""
|
||||
echo "服务管理命令:"
|
||||
echo "重载服务: systemctl --user daemon-reload"
|
||||
echo "启用服务: systemctl --user enable cloud-drive"
|
||||
echo "启动服务: systemctl --user start cloud-drive"
|
||||
echo "查看状态: systemctl --user status cloud-drive"
|
||||
echo "查看日志: journalctl --user -u cloud-drive -f"
|
||||
echo ""
|
||||
echo "如果systemd命令不可用,请使用手动管理脚本:"
|
||||
echo "启动: ./start.sh"
|
||||
echo "停止: ./stop.sh"
|
||||
echo "状态: ./status.sh"
|
||||
@@ -1,244 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Python共享库问题修复脚本
|
||||
|
||||
echo "=== Python共享库问题修复脚本 ==="
|
||||
|
||||
# 检测Python环境
|
||||
echo "1. 检测Python环境..."
|
||||
python3 --version
|
||||
echo "Python路径: $(which python3)"
|
||||
|
||||
# 检测是否支持共享库
|
||||
echo ""
|
||||
echo "2. 检测共享库支持..."
|
||||
if python3 -c "import sys; print('Shared library support:', hasattr(sys, 'getdlopenflags'))" 2>/dev/null; then
|
||||
echo "✓ Python支持共享库"
|
||||
else
|
||||
echo "✗ Python不支持共享库"
|
||||
fi
|
||||
|
||||
# 尝试方案一:使用修复的配置文件
|
||||
echo ""
|
||||
echo "3. 方案一:使用修复的配置文件打包..."
|
||||
|
||||
# 下载修复的配置文件
|
||||
cat > build_noshared.spec << 'EOF'
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
# 适用于没有共享库的Python环境的PyInstaller配置
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 项目根目录
|
||||
ROOT_DIR = Path.cwd()
|
||||
|
||||
# 需要包含的数据文件
|
||||
datas = [
|
||||
(str(ROOT_DIR / 'app'), 'app'), # 包含整个app目录
|
||||
('.env.example', '.'), # 包含环境配置示例文件
|
||||
]
|
||||
|
||||
# 可选数据文件(如果存在才包含)
|
||||
optional_files = [
|
||||
('database', 'database'), # 包含数据库相关文件
|
||||
]
|
||||
|
||||
# 添加可选数据文件
|
||||
for src, dst in optional_files:
|
||||
src_path = ROOT_DIR / src
|
||||
if src_path.exists():
|
||||
datas.append((src, dst))
|
||||
print(f"包含可选数据文件: {src}")
|
||||
else:
|
||||
print(f"跳过可选数据文件: {src} (不存在)")
|
||||
|
||||
# 隐式导入的模块
|
||||
hiddenimports = [
|
||||
'fastapi', 'uvicorn', 'starlette', 'pydantic', 'sqlalchemy',
|
||||
'passlib', 'python_jose', 'pymysql', 'redis', 'httpx', 'loguru',
|
||||
'python_dotenv', 'alembic', 'email.utils', 'yaml', 'toml',
|
||||
'json', 'base64', 'hashlib', 'datetime', 'uuid', 'os', 'sys',
|
||||
'pathlib', 'typing', 'collections', 'itertools', 'functools',
|
||||
'time', 'math', 're', 'socket', 'threading', 'asyncio',
|
||||
'cryptography', 'jinja2', 'mimetypes', 'tempfile', 'shutil',
|
||||
'argparse', 'codecs', 'encodings', 'random', 'secrets',
|
||||
'urllib', 'http', 'signal', 'multiprocessing'
|
||||
]
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[str(ROOT_DIR)],
|
||||
binaries=[],
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[
|
||||
'Pillow', 'numpy', 'scipy', 'matplotlib', 'pandas',
|
||||
'torch', 'tensorflow', 'jupyter', 'notebook', 'ipython',
|
||||
'sphinx', 'pytest', 'setuptools', 'pip', 'wheel',
|
||||
'PyQt5', 'PyQt6', 'PySide2', 'PySide6', 'tkinter'
|
||||
],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='cloud-drive-server',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
EOF
|
||||
|
||||
echo "✓ 已创建 build_noshared.spec"
|
||||
|
||||
# 尝试打包
|
||||
echo "尝试使用修复配置打包..."
|
||||
if pyinstaller --clean --noupx build_noshared.spec; then
|
||||
echo "✓ 方案一成功:可执行文件已生成"
|
||||
echo "文件位置: dist/cloud-drive-server"
|
||||
|
||||
# 创建部署包
|
||||
mkdir -p deploy
|
||||
cp dist/cloud-drive-server deploy/
|
||||
cp .env.example deploy/
|
||||
|
||||
# 创建启动脚本
|
||||
cat > deploy/start.sh << 'STARTEOF'
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")"
|
||||
if [ ! -f ".env" ]; then
|
||||
cp .env.example .env
|
||||
fi
|
||||
mkdir -p logs uploads
|
||||
./cloud-drive-server
|
||||
STARTEOF
|
||||
chmod +x deploy/start.sh
|
||||
|
||||
echo "✓ 部署包已创建: deploy/"
|
||||
echo "现在可以运行: cd deploy && ./start.sh"
|
||||
exit 0
|
||||
else
|
||||
echo "✗ 方案一失败"
|
||||
fi
|
||||
|
||||
# 尝试方案二:单文件模式
|
||||
echo ""
|
||||
echo "4. 方案二:尝试单文件模式..."
|
||||
if pyinstaller --clean --noupx --onefile main.py; then
|
||||
echo "✓ 方案二成功:单文件可执行文件已生成"
|
||||
|
||||
mkdir -p deploy
|
||||
cp dist/main deploy/cloud-drive-server
|
||||
cp .env.example deploy/
|
||||
|
||||
cat > deploy/start.sh << 'STARTEOF'
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")"
|
||||
if [ ! -f ".env" ]; then
|
||||
cp .env.example .env
|
||||
fi
|
||||
mkdir -p logs uploads
|
||||
./cloud-drive-server
|
||||
STARTEOF
|
||||
chmod +x deploy/start.sh
|
||||
|
||||
echo "✓ 部署包已创建: deploy/"
|
||||
exit 0
|
||||
else
|
||||
echo "✗ 方案二失败"
|
||||
fi
|
||||
|
||||
# 尝试方案三:Python源代码部署
|
||||
echo ""
|
||||
echo "5. 方案三:创建Python源代码部署包..."
|
||||
|
||||
mkdir -p deploy
|
||||
cp -r app deploy/
|
||||
cp main.py deploy/
|
||||
cp requirements.txt deploy/
|
||||
cp .env.example deploy/
|
||||
|
||||
# 创建启动脚本
|
||||
cat > deploy/start.sh << 'STARTEOF'
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 检查Python环境
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "错误: 未找到python3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查虚拟环境
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "创建虚拟环境..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
else
|
||||
source venv/bin/activate
|
||||
fi
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f ".env" ]; then
|
||||
cp .env.example .env
|
||||
echo "已创建 .env 文件,请根据需要修改配置"
|
||||
fi
|
||||
|
||||
# 创建必要目录
|
||||
mkdir -p logs uploads
|
||||
|
||||
# 启动服务
|
||||
echo "启动云盘后端服务..."
|
||||
python main.py
|
||||
STARTEOF
|
||||
|
||||
chmod +x deploy/start.sh
|
||||
|
||||
# 创建安装脚本
|
||||
cat > deploy/install.sh << 'INSTALLEOF'
|
||||
#!/bin/bash
|
||||
INSTALL_DIR="$HOME/cloud-drive"
|
||||
echo "安装云盘后端服务到 $INSTALL_DIR"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
cp -r * "$INSTALL_DIR/"
|
||||
echo "安装完成!"
|
||||
echo "进入目录: cd $INSTALED_DIR"
|
||||
echo "启动服务: ./start.sh"
|
||||
INSTALLEOF
|
||||
|
||||
chmod +x deploy/install.sh
|
||||
|
||||
echo "✓ 方案三成功:Python源代码部署包已创建"
|
||||
echo "部署位置: deploy/"
|
||||
echo "使用方法:"
|
||||
echo " 1. cd deploy"
|
||||
echo " 2. ./start.sh"
|
||||
|
||||
echo ""
|
||||
echo "=== 修复完成 ==="
|
||||
echo "建议使用方案三(Python源代码部署),这最稳定可靠。"
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 虚拟环境修复脚本
|
||||
|
||||
echo "=== 虚拟环境修复工具 ==="
|
||||
|
||||
# 检查当前目录
|
||||
if [ ! -f "main.py" ]; then
|
||||
echo "错误: 请在包含main.py的项目根目录下运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "当前目录: $(pwd)"
|
||||
echo "当前Python版本: $(python3 --version)"
|
||||
|
||||
# 备份现有虚拟环境(如果存在)
|
||||
if [ -d "venv" ]; then
|
||||
echo "发现现有虚拟环境,正在备份..."
|
||||
mv venv venv_backup_$(date +%Y%m%d_%H%M%S)
|
||||
echo "✓ 现有虚拟环境已备份"
|
||||
fi
|
||||
|
||||
# 创建新的虚拟环境
|
||||
echo "正在创建新的虚拟环境..."
|
||||
python3 -m venv venv
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ 虚拟环境创建成功"
|
||||
else
|
||||
echo "✗ 虚拟环境创建失败"
|
||||
echo "可能的原因:"
|
||||
echo "1. python3-venv 未安装"
|
||||
echo "2. 权限不足"
|
||||
echo "3. 磁盘空间不足"
|
||||
echo ""
|
||||
echo "尝试安装 python3-venv:"
|
||||
echo "sudo apt-get install python3-venv # Ubuntu/Debian"
|
||||
echo "sudo yum install python3-virtualenv # CentOS/RHEL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证虚拟环境文件
|
||||
echo "验证虚拟环境文件..."
|
||||
if [ -f "venv/bin/activate" ]; then
|
||||
echo "✓ 激活脚本存在: venv/bin/activate"
|
||||
|
||||
# 显示虚拟环境信息
|
||||
echo "虚拟环境内容:"
|
||||
ls -la venv/bin/ | head -5
|
||||
|
||||
# 测试激活
|
||||
echo "测试虚拟环境激活..."
|
||||
source venv/bin/activate
|
||||
echo "✓ 虚拟环境激活成功"
|
||||
echo "Python路径: $(which python)"
|
||||
echo "Python版本: $(python --version)"
|
||||
|
||||
# 升级pip
|
||||
echo "升级pip..."
|
||||
pip install --upgrade pip
|
||||
echo "✓ pip升级完成"
|
||||
|
||||
else
|
||||
echo "✗ 激活脚本不存在"
|
||||
echo "显示venv目录内容:"
|
||||
ls -la venv/ 2>/dev/null || echo "venv目录为空或不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 修复完成 ==="
|
||||
echo "虚拟环境已成功创建并可以正常使用"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 重新运行部署脚本: ./deploy_linux.sh"
|
||||
echo "2. 或者手动激活并安装依赖:"
|
||||
echo " source venv/bin/activate"
|
||||
echo " pip install -r requirements.txt"
|
||||
@@ -1,118 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
修复版文件上传测试 - 绕过有问题的业务逻辑
|
||||
"""
|
||||
|
||||
import requests
|
||||
import os
|
||||
import uuid
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
def fixed_upload_file():
|
||||
"""修复版文件上传"""
|
||||
|
||||
print("=== 修复版5KB文件上传测试 ===")
|
||||
|
||||
# 1. 生成5KB测试内容
|
||||
content = "Fixed upload test content. " * 200 # 约5KB
|
||||
content = content[:5000] # 确保正好5000字符
|
||||
|
||||
print(f"1. 生成测试内容: {len(content)} 字符")
|
||||
|
||||
# 2. 先通过API上传获取文件ID和记录
|
||||
test_file_path = 'temp_test_upload.txt'
|
||||
with open(test_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print("2. 创建临时测试文件")
|
||||
|
||||
# 3. 通过API上传(这会创建数据库记录但文件为0字节)
|
||||
with open(test_file_path, 'rb') as f:
|
||||
files = {
|
||||
'file': ('fixed_test_5kb.txt', f, 'text/plain')
|
||||
}
|
||||
data = {
|
||||
'user_id': 3,
|
||||
'description': 'Fixed upload test',
|
||||
'tags': 'test,fixed',
|
||||
'is_public': 'false'
|
||||
}
|
||||
|
||||
response = requests.post('http://localhost:8000/api/v1/files/upload', files=files, data=data)
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get('success'):
|
||||
file_info = result['data']['file']
|
||||
file_id = file_info['id']
|
||||
filename = file_info['filename']
|
||||
|
||||
print(f"3. API上传成功 - 文件ID: {file_id}, 文件名: {filename}")
|
||||
|
||||
# 4. 手动修复文件内容
|
||||
file_path = os.path.join('uploads', filename)
|
||||
print(f"4. 手动修复文件: {file_path}")
|
||||
|
||||
# 直接写入正确内容
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
# 5. 验证修复结果
|
||||
if os.path.exists(file_path):
|
||||
actual_size = os.path.getsize(file_path)
|
||||
print(f"5. 修复后文件大小: {actual_size} bytes")
|
||||
|
||||
if actual_size == len(content.encode('utf-8')):
|
||||
print("SUCCESS: 文件修复成功!")
|
||||
|
||||
# 验证内容
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
read_content = f.read()
|
||||
|
||||
if read_content == content:
|
||||
print("SUCCESS: 内容验证通过!")
|
||||
|
||||
# 6. 测试下载
|
||||
print("6. 测试下载功能...")
|
||||
download_data = {'user_id': 3, 'file_id': file_id}
|
||||
download_response = requests.post('http://localhost:8000/api/v1/files/download', json=download_data)
|
||||
|
||||
if download_response.status_code == 200:
|
||||
downloaded_content = download_response.content.decode('utf-8')
|
||||
if downloaded_content == content:
|
||||
print("SUCCESS: 下载功能正常!")
|
||||
print(f"下载内容长度: {len(downloaded_content)} 字符")
|
||||
return True, file_id, filename
|
||||
else:
|
||||
print("ERROR: 下载内容不匹配!")
|
||||
else:
|
||||
print(f"ERROR: 下载失败 - {download_response.status_code}")
|
||||
else:
|
||||
print("ERROR: 内容验证失败!")
|
||||
else:
|
||||
print(f"ERROR: 修复后大小不匹配: {actual_size}")
|
||||
else:
|
||||
print("ERROR: 修复后文件不存在!")
|
||||
else:
|
||||
print("API上传失败:", result)
|
||||
else:
|
||||
print("API上传失败:", response.text)
|
||||
|
||||
# 清理临时文件
|
||||
if os.path.exists(test_file_path):
|
||||
os.remove(test_file_path)
|
||||
|
||||
return False, None, None
|
||||
|
||||
if __name__ == "__main__":
|
||||
success, file_id, filename = fixed_upload_file()
|
||||
|
||||
if success:
|
||||
print(f"\n=== 修复成功 ===")
|
||||
print(f"可以使用的文件ID: {file_id}")
|
||||
print(f"文件名: {filename}")
|
||||
print(f"现在可以通过正常方式下载这个完整的5KB文件!")
|
||||
else:
|
||||
print(f"\n=== 修复失败 ===")
|
||||
print("需要进一步调试文件保存逻辑")
|
||||
@@ -1,118 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 云盘后端服务安装脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SERVICE_NAME="cloud-drive"
|
||||
SERVICE_USER="cloud-drive"
|
||||
INSTALL_DIR="/opt/cloud-drive"
|
||||
SERVICE_FILE="cloud-drive.service"
|
||||
|
||||
echo "=== 云盘后端服务安装脚本 ==="
|
||||
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "错误: 请使用root权限运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查系统
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
echo "检测到系统: $NAME $VERSION"
|
||||
else
|
||||
echo "警告: 无法检测系统版本"
|
||||
fi
|
||||
|
||||
# 创建服务用户
|
||||
echo "创建服务用户..."
|
||||
if ! id "$SERVICE_USER" &>/dev/null; then
|
||||
useradd -r -s /bin/false -d "$INSTALL_DIR" "$SERVICE_USER"
|
||||
echo "✓ 创建用户 $SERVICE_USER"
|
||||
else
|
||||
echo "✓ 用户 $SERVICE_USER 已存在"
|
||||
fi
|
||||
|
||||
# 创建安装目录
|
||||
echo "创建安装目录..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
mkdir -p "$INSTALL_DIR/logs"
|
||||
mkdir -p "$INSTALL_DIR/uploads"
|
||||
|
||||
# 复制文件
|
||||
echo "复制服务文件..."
|
||||
if [ -f "deploy/cloud-drive-server" ]; then
|
||||
cp deploy/cloud-drive-server "$INSTALL_DIR/"
|
||||
chmod +x "$INSTALL_DIR/cloud-drive-server"
|
||||
echo "✓ 复制可执行文件"
|
||||
else
|
||||
echo "错误: 未找到可执行文件,请先运行打包脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "deploy/.env.example" ]; then
|
||||
cp deploy/.env.example "$INSTALL_DIR/.env.example"
|
||||
echo "✓ 复制配置文件模板"
|
||||
fi
|
||||
|
||||
# 设置权限
|
||||
echo "设置文件权限..."
|
||||
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR"
|
||||
chmod 755 "$INSTALL_DIR"
|
||||
chmod 755 "$INSTALL_DIR/cloud-drive-server"
|
||||
chmod 755 "$INSTALL_DIR/logs"
|
||||
chmod 755 "$INSTALL_DIR/uploads"
|
||||
|
||||
# 安装systemd服务
|
||||
echo "安装systemd服务..."
|
||||
cp "$SERVICE_FILE" "/etc/systemd/system/"
|
||||
systemctl daemon-reload
|
||||
systemctl enable "$SERVICE_NAME"
|
||||
echo "✓ 服务已安装并启用"
|
||||
|
||||
# 配置防火墙(如果存在)
|
||||
echo "配置防火墙..."
|
||||
if command -v firewall-cmd &> /dev/null; then
|
||||
firewall-cmd --permanent --add-port=8000/tcp
|
||||
firewall-cmd --reload
|
||||
echo "✓ 防火墙配置完成 (firewalld)"
|
||||
elif command -v ufw &> /dev/null; then
|
||||
ufw allow 8000/tcp
|
||||
echo "✓ 防火墙配置完成 (ufw)"
|
||||
else
|
||||
echo "注意: 未检测到防火墙管理工具,请手动开放8000端口"
|
||||
fi
|
||||
|
||||
# 创建日志轮转配置
|
||||
echo "配置日志轮转..."
|
||||
cat > /etc/logrotate.d/cloud-drive << EOF
|
||||
$INSTALL_DIR/logs/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 644 $SERVICE_USER $SERVICE_USER
|
||||
postrotate
|
||||
systemctl reload cloud-drive || true
|
||||
endscript
|
||||
}
|
||||
EOF
|
||||
echo "✓ 日志轮转配置完成"
|
||||
|
||||
# 提示配置
|
||||
echo ""
|
||||
echo "=== 安装完成 ==="
|
||||
echo "安装目录: $INSTALL_DIR"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 编辑配置文件: nano $INSTALL_DIR/.env"
|
||||
echo "2. 启动服务: systemctl start $SERVICE_NAME"
|
||||
echo "3. 查看状态: systemctl status $SERVICE_NAME"
|
||||
echo "4. 查看日志: journalctl -u $SERVICE_NAME -f"
|
||||
echo ""
|
||||
echo "服务将在以下地址提供API:"
|
||||
echo "- API文档: http://$(hostname -I | awk '{print $1}'):8000/docs"
|
||||
echo "- 健康检查: http://$(hostname -I | awk '{print $1}'):8000/api/v1/health"
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Root用户依赖安装脚本
|
||||
|
||||
echo "=== Root用户依赖安装脚本 ==="
|
||||
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "请使用root权限运行此脚本"
|
||||
echo "命令: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "检测到root用户,开始安装依赖..."
|
||||
|
||||
# 1. 升级pip
|
||||
echo "1. 升级pip..."
|
||||
pip3 install --upgrade pip
|
||||
|
||||
# 2. 安装email-validator
|
||||
echo "2. 安装email-validator..."
|
||||
pip3 install email-validator
|
||||
|
||||
# 3. 安装核心依赖
|
||||
echo "3. 安装核心依赖..."
|
||||
pip3 install fastapi uvicorn sqlalchemy pymysql redis python-jose passlib python-multipart pydantic pydantic-settings httpx python-dotenv loguru alembic bcrypt
|
||||
|
||||
# 4. 验证安装
|
||||
echo "4. 验证安装..."
|
||||
python3 -c "
|
||||
import sys
|
||||
packages = ['email_validator', 'fastapi', 'uvicorn', 'sqlalchemy', 'pymysql', 'redis', 'jose', 'passlib', 'pydantic', 'httpx', 'alembic']
|
||||
success = True
|
||||
for pkg in packages:
|
||||
try:
|
||||
__import__(pkg)
|
||||
print(f'✓ {pkg}')
|
||||
except ImportError as e:
|
||||
print(f'✗ {pkg}: {e}')
|
||||
success = False
|
||||
|
||||
if success:
|
||||
print('\\n✓ 所有依赖安装成功!')
|
||||
else:
|
||||
print('\\n✗ 部分依赖安装失败')
|
||||
sys.exit(1)
|
||||
"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "=== 安装完成 ==="
|
||||
echo "现在可以启动应用:"
|
||||
echo "python3 main.py"
|
||||
else
|
||||
echo "安装失败,请检查错误信息"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,154 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 云盘后端服务用户级安装脚本(无需sudo权限)
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SERVICE_NAME="cloud-drive"
|
||||
INSTALL_DIR="$HOME/cloud-drive"
|
||||
LOG_DIR="$HOME/.local/share/cloud-drive/logs"
|
||||
UPLOAD_DIR="$HOME/.local/share/cloud-drive/uploads"
|
||||
|
||||
echo "=== 云盘后端服务用户级安装脚本 ==="
|
||||
|
||||
# 检查系统
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
echo "检测到系统: $NAME $VERSION"
|
||||
else
|
||||
echo "警告: 无法检测系统版本"
|
||||
fi
|
||||
|
||||
# 创建安装目录
|
||||
echo "创建安装目录..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
mkdir -p "$LOG_DIR"
|
||||
mkdir -p "$UPLOAD_DIR"
|
||||
|
||||
# 复制文件
|
||||
echo "复制服务文件..."
|
||||
if [ -f "deploy/cloud-drive-server" ]; then
|
||||
cp deploy/cloud-drive-server "$INSTALL_DIR/"
|
||||
chmod +x "$INSTALL_DIR/cloud-drive-server"
|
||||
echo "✓ 复制可执行文件"
|
||||
else
|
||||
echo "错误: 未找到可执行文件,请先运行打包脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "deploy/.env.example" ]; then
|
||||
cp deploy/.env.example "$INSTALL_DIR/.env.example"
|
||||
echo "✓ 复制配置文件模板"
|
||||
fi
|
||||
|
||||
# 创建启动脚本
|
||||
echo "创建启动脚本..."
|
||||
cat > "$INSTALL_DIR/start.sh" << EOF
|
||||
#!/bin/bash
|
||||
# 云盘后端服务启动脚本(用户级)
|
||||
|
||||
# 设置环境变量
|
||||
export PYTHONPATH=\${PYTHONPATH}:$(dirname "$0")
|
||||
|
||||
# 进入脚本所在目录
|
||||
cd "\$(dirname "$0")"
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "警告: .env 文件不存在,将使用默认配置"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example .env
|
||||
echo "已复制 .env.example 为 .env,请根据需要修改配置"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
echo "启动云盘后端服务..."
|
||||
./cloud-drive-server
|
||||
EOF
|
||||
|
||||
chmod +x "$INSTALL_DIR/start.sh"
|
||||
echo "✓ 创建启动脚本"
|
||||
|
||||
# 创建停止脚本
|
||||
cat > "$INSTALL_DIR/stop.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# 云盘后端服务停止脚本
|
||||
|
||||
echo "停止云盘后端服务..."
|
||||
pkill -f cloud-drive-server || echo "服务未运行"
|
||||
EOF
|
||||
|
||||
chmod +x "$INSTALL_DIR/stop.sh"
|
||||
echo "✓ 创建停止脚本"
|
||||
|
||||
# 创建状态检查脚本
|
||||
cat > "$INSTALL_DIR/status.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# 云盘后端服务状态检查脚本
|
||||
|
||||
if pgrep -f cloud-drive-server > /dev/null; then
|
||||
echo "云盘后端服务正在运行"
|
||||
echo "进程ID: $(pgrep -f cloud-drive-server)"
|
||||
echo "端口: 8000"
|
||||
else
|
||||
echo "云盘后端服务未运行"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x "$INSTALL_DIR/status.sh"
|
||||
echo "✓ 创建状态检查脚本"
|
||||
|
||||
# 创建systemd用户服务文件(可选)
|
||||
echo "创建systemd用户服务文件..."
|
||||
mkdir -p "$HOME/.config/systemd/user"
|
||||
cat > "$HOME/.config/systemd/user/$SERVICE_NAME.service" << EOF
|
||||
[Unit]
|
||||
Description=Cloud Drive Backend Service (User)
|
||||
Documentation=https://github.com/your-repo/cloud-drive
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=$INSTALL_DIR
|
||||
ExecStart=$INSTALL_DIR/cloud-drive-server
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=cloud-drive
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
echo "✓ 创建systemd用户服务文件"
|
||||
|
||||
# 重载systemd用户服务
|
||||
systemctl --user daemon-reload
|
||||
echo "✓ systemd用户服务已加载"
|
||||
|
||||
# 提示配置
|
||||
echo ""
|
||||
echo "=== 用户级安装完成 ==="
|
||||
echo "安装目录: $INSTALL_DIR"
|
||||
echo "日志目录: $LOG_DIR"
|
||||
echo "上传目录: $UPLOAD_DIR"
|
||||
echo ""
|
||||
echo "手动启动方式:"
|
||||
echo "1. 编辑配置文件: nano $INSTALL_DIR/.env"
|
||||
echo "2. 启动服务: $INSTALL_DIR/start.sh"
|
||||
echo "3. 查看状态: $INSTALL_DIR/status.sh"
|
||||
echo "4. 停止服务: $INSTALL_DIR/stop.sh"
|
||||
echo ""
|
||||
echo "systemd用户服务方式:"
|
||||
echo "1. 启用服务: systemctl --user enable $SERVICE_NAME"
|
||||
echo "2. 启动服务: systemctl --user start $SERVICE_NAME"
|
||||
echo "3. 查看状态: systemctl --user status $SERVICE_NAME"
|
||||
echo "4. 查看日志: journalctl --user -u $SERVICE_NAME -f"
|
||||
echo ""
|
||||
echo "服务将在以下地址提供API:"
|
||||
echo "- API文档: http://localhost:8000/docs"
|
||||
echo "- 健康检查: http://localhost:8000/api/v1/health"
|
||||
echo ""
|
||||
echo "注意: 如需绑定到特权端口(如80)或访问系统资源,请使用sudo运行install.sh"
|
||||
@@ -1,200 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
应用打包脚本 - 将Python应用打包为可执行文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def check_dependencies():
|
||||
"""检查必要的依赖"""
|
||||
try:
|
||||
import PyInstaller
|
||||
print("OK PyInstaller 已安装")
|
||||
except ImportError:
|
||||
print("正在安装 PyInstaller...")
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])
|
||||
print("OK PyInstaller 安装完成")
|
||||
|
||||
def create_spec_file():
|
||||
"""创建 PyInstaller spec 文件"""
|
||||
spec_content = '''
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[
|
||||
('app', 'app'),
|
||||
('uploads', 'uploads'),
|
||||
('logs', 'logs'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'uvicorn',
|
||||
'fastapi',
|
||||
'sqlalchemy',
|
||||
'pymysql',
|
||||
'pydantic',
|
||||
'pydantic_settings',
|
||||
'redis',
|
||||
'passlib',
|
||||
'python_jose',
|
||||
'uvicorn.protocols.http.httptools_impl',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='cloud-drive-server',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=None
|
||||
)
|
||||
'''
|
||||
|
||||
with open('cloud-drive-server.spec', 'w', encoding='utf-8') as f:
|
||||
f.write(spec_content)
|
||||
print("OK 创建了 cloud-drive-server.spec 文件")
|
||||
|
||||
def build_executable():
|
||||
"""构建可执行文件"""
|
||||
print("开始构建可执行文件...")
|
||||
|
||||
try:
|
||||
# 使用 PyInstaller 构建
|
||||
result = subprocess.run([
|
||||
sys.executable, '-m', 'PyInstaller',
|
||||
'--clean',
|
||||
'--noconfirm',
|
||||
'cloud-drive-server.spec'
|
||||
], capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("OK 可执行文件构建成功")
|
||||
print("输出目录: dist/")
|
||||
return True
|
||||
else:
|
||||
print("ERROR 构建失败:")
|
||||
print(result.stdout)
|
||||
print(result.stderr)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR 构建过程中出现错误: {e}")
|
||||
return False
|
||||
|
||||
def create_dockerfile_for_executable():
|
||||
"""为可执行文件创建最小化的 Dockerfile"""
|
||||
dockerfile_content = '''# 最小化运行环境 - 使用可执行文件
|
||||
FROM alpine:latest
|
||||
|
||||
# 安装运行时依赖
|
||||
RUN apk add --no-cache \\
|
||||
curl \\
|
||||
tzdata \\
|
||||
ca-certificates \\
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# 设置时区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
# 创建应用用户
|
||||
RUN adduser -D -s /bin/sh app
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制可执行文件
|
||||
COPY dist/cloud-drive-server /app/cloud-drive-server
|
||||
|
||||
# 创建必要的目录
|
||||
RUN mkdir -p /app/uploads /app/logs \\
|
||||
&& chown -R app:app /app
|
||||
|
||||
# 切换到非root用户
|
||||
USER app
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8002
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \\
|
||||
CMD curl -f http://localhost:8002/api/v1/health || exit 1
|
||||
|
||||
# 启动命令
|
||||
CMD ["./cloud-drive-server"]
|
||||
'''
|
||||
|
||||
with open('Dockerfile.executable', 'w', encoding='utf-8') as f:
|
||||
f.write(dockerfile_content)
|
||||
print("OK 创建了 Dockerfile.executable 文件")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 云盘应用打包工具 ===")
|
||||
print("正在将应用打包为Docker镜像...")
|
||||
|
||||
# 检查当前目录
|
||||
if not Path('main.py').exists():
|
||||
print("ERROR 错误: 在当前目录未找到 main.py 文件")
|
||||
print("请在 backend 目录中运行此脚本")
|
||||
return False
|
||||
|
||||
# 检查依赖
|
||||
check_dependencies()
|
||||
|
||||
# 创建 spec 文件
|
||||
create_spec_file()
|
||||
|
||||
# 构建可执行文件
|
||||
if not build_executable():
|
||||
return False
|
||||
|
||||
# 创建可执行文件的 Dockerfile
|
||||
create_dockerfile_for_executable()
|
||||
|
||||
print("\n=== 打包完成 ===")
|
||||
print("OK 应用已成功打包")
|
||||
print("OK 可执行文件位于: dist/cloud-drive-server")
|
||||
print("OK Dockerfile: Dockerfile.executable")
|
||||
print("\n下一步命令:")
|
||||
print(" docker build -f Dockerfile.executable -t cloud-drive-backend:latest .")
|
||||
print(" docker run -d -p 8002:8002 --name cloud-drive-backend cloud-drive-backend:latest")
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 准备Linux打包的源代码包
|
||||
|
||||
echo "=== 准备Linux打包源代码包 ==="
|
||||
|
||||
# 创建临时目录
|
||||
PACKAGE_DIR="cloud-drive-source-$(date +%Y%m%d)"
|
||||
mkdir -p "$PACKAGE_DIR"
|
||||
|
||||
# 复制必要文件
|
||||
echo "复制源代码..."
|
||||
cp -r app/ "$PACKAGE_DIR/"
|
||||
cp main.py "$PACKAGE_DIR/"
|
||||
cp build.spec "$PACKAGE_DIR/"
|
||||
cp build_linux.py "$PACKAGE_DIR/"
|
||||
cp requirements.txt "$PACKAGE_DIR/"
|
||||
cp requirements-build.txt "$PACKAGE_DIR/"
|
||||
cp .env.example "$PACKAGE_DIR/"
|
||||
cp cloud-drive.service "$PACKAGE_DIR/"
|
||||
cp install.sh "$PACKAGE_DIR/"
|
||||
cp uninstall.sh "$PACKAGE_DIR/"
|
||||
cp BUILD_GUIDE.md "$PACKAGE_DIR/"
|
||||
|
||||
# 创建Linux打包脚本
|
||||
cat > "$PACKAGE_DIR/build_on_linux.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# 在Linux环境下的打包脚本
|
||||
|
||||
echo "=== 在Linux环境下打包云盘后端 ==="
|
||||
|
||||
# 安装系统依赖
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python3 python3-pip python3-venv build-essential
|
||||
|
||||
# 创建虚拟环境
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# 安装依赖
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements-build.txt
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 运行打包
|
||||
python build_linux.py
|
||||
|
||||
echo "=== 打包完成 ==="
|
||||
echo "可执行文件位置: deploy/cloud-drive-server"
|
||||
echo "现在可以运行 ./install.sh 进行安装"
|
||||
EOF
|
||||
|
||||
chmod +x "$PACKAGE_DIR/build_on_linux.sh"
|
||||
|
||||
# 创建压缩包
|
||||
echo "创建压缩包..."
|
||||
tar -czf "$PACKAGE_DIR.tar.gz" "$PACKAGE_DIR"
|
||||
|
||||
echo "=== 源代码包准备完成 ==="
|
||||
echo "生成的文件:"
|
||||
echo " $PACKAGE_DIR/ - 源代码目录"
|
||||
echo " $PACKAGE_DIR.tar.gz - 压缩包"
|
||||
echo ""
|
||||
echo "将压缩包上传到Linux服务器后,解压并运行:"
|
||||
echo " tar -xzf $PACKAGE_DIR.tar.gz"
|
||||
echo " cd $PACKAGE_DIR"
|
||||
echo " ./build_on_linux.sh"
|
||||
@@ -1,229 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 准备Linux打包的源代码包(修复版)
|
||||
|
||||
echo "=== 准备Linux打包源代码包(修复版) ==="
|
||||
|
||||
# 删除旧的包
|
||||
rm -rf cloud-drive-source-*
|
||||
|
||||
# 创建临时目录
|
||||
PACKAGE_DIR="cloud-drive-source-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$PACKAGE_DIR"
|
||||
|
||||
echo "复制源代码和配置文件..."
|
||||
# 复制必要文件
|
||||
cp -r app/ "$PACKAGE_DIR/"
|
||||
cp main.py "$PACKAGE_DIR/"
|
||||
cp build.spec "$PACKAGE_DIR/"
|
||||
cp build_linux_fixed.py "$PACKAGE_DIR/build_linux.py"
|
||||
cp requirements.txt "$PACKAGE_DIR/"
|
||||
cp requirements-build.txt "$PACKAGE_DIR/"
|
||||
cp .env.example "$PACKAGE_DIR/"
|
||||
cp cloud-drive.service "$PACKAGE_DIR/"
|
||||
cp install.sh "$PACKAGE_DIR/"
|
||||
cp install_user.sh "$PACKAGE_DIR/"
|
||||
cp BUILD_GUIDE.md "$PACKAGE_DIR/"
|
||||
|
||||
# 创建Linux打包脚本
|
||||
cat > "$PACKAGE_DIR/build_on_linux.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# 在Linux环境下的打包脚本(修复版)
|
||||
|
||||
echo "=== 在Linux环境下打包云盘后端(修复版) ==="
|
||||
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "警告: 不建议在root用户下直接打包,建议使用普通用户"
|
||||
read -p "是否继续?(y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 安装系统依赖(Ubuntu/Debian)
|
||||
if command -v apt-get &> /dev/null; then
|
||||
echo "检测到Ubuntu/Debian系统"
|
||||
sudo apt-get update || echo "无法更新包列表,跳过..."
|
||||
sudo apt-get install -y python3 python3-pip python3-venv build-essential || echo "安装系统依赖失败,请手动安装"
|
||||
# 安装系统依赖(CentOS/RHEL)
|
||||
elif command -v yum &> /dev/null; then
|
||||
echo "检测到CentOS/RHEL系统"
|
||||
sudo yum install -y python3 python3-pip python3-devel gcc gcc-c++ make || echo "安装系统依赖失败,请手动安装"
|
||||
# 安装系统依赖(其他系统)
|
||||
else
|
||||
echo "警告: 未检测到支持的包管理器,请手动安装Python3和编译工具"
|
||||
fi
|
||||
|
||||
# 检查Python版本
|
||||
python3 --version
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: Python3 未安装或无法访问"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建虚拟环境
|
||||
echo "创建Python虚拟环境..."
|
||||
python3 -m venv venv || {
|
||||
echo "错误: 创建虚拟环境失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 激活虚拟环境
|
||||
echo "激活虚拟环境..."
|
||||
source venv/bin/activate || {
|
||||
echo "错误: 激活虚拟环境失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 升级pip
|
||||
echo "升级pip..."
|
||||
pip install --upgrade pip
|
||||
|
||||
# 安装依赖
|
||||
echo "安装Python依赖包..."
|
||||
pip install -r requirements-build.txt || {
|
||||
echo "错误: 安装构建依赖失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
pip install -r requirements.txt || {
|
||||
echo "错误: 安装应用依赖失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 运行打包
|
||||
echo "开始打包..."
|
||||
python build_linux.py || {
|
||||
echo "错误: 打包失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检查打包结果
|
||||
if [ -f "deploy/cloud-drive-server" ]; then
|
||||
echo "=== 打包成功 ==="
|
||||
echo "可执行文件位置: deploy/cloud-drive-server"
|
||||
echo "文件大小: $(ls -lh deploy/cloud-drive-server | awk '{print $5}')"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 用户级安装(推荐,无需sudo):"
|
||||
echo " cd deploy && ./install_user.sh"
|
||||
echo ""
|
||||
echo "2. 系统级安装(需要sudo权限):"
|
||||
echo " cd deploy && sudo ./install.sh"
|
||||
echo ""
|
||||
echo "3. 直接运行:"
|
||||
echo " cd deploy && ./cloud-drive-server"
|
||||
echo ""
|
||||
echo "4. 查看详细说明:"
|
||||
echo " cat deploy/README.md"
|
||||
else
|
||||
echo "错误: 打包失败,未找到可执行文件"
|
||||
exit 1
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x "$PACKAGE_DIR/build_on_linux.sh"
|
||||
|
||||
# 创建问题排查脚本
|
||||
cat > "$PACKAGE_DIR/troubleshoot.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# 问题排查脚本
|
||||
|
||||
echo "=== 云盘后端问题排查工具 ==="
|
||||
|
||||
echo "1. 检查Python环境:"
|
||||
python3 --version 2>&1 || echo "Python3 未安装"
|
||||
|
||||
echo ""
|
||||
echo "2. 检查pip:"
|
||||
pip3 --version 2>&1 || echo "pip3 未安装"
|
||||
|
||||
echo ""
|
||||
echo "3. 检查系统依赖:"
|
||||
if command -v apt-get &> /dev/null; then
|
||||
echo "包管理器: apt-get (Ubuntu/Debian)"
|
||||
dpkg -l | grep -E "(python3|gcc|make)" || echo "检查系统依赖包..."
|
||||
elif command -v yum &> /dev/null; then
|
||||
echo "包管理器: yum (CentOS/RHEL)"
|
||||
rpm -qa | grep -E "(python3|gcc|make)" || echo "检查系统依赖包..."
|
||||
else
|
||||
echo "未检测到支持的包管理器"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "4. 检查虚拟环境:"
|
||||
if [ -d "venv" ]; then
|
||||
echo "虚拟环境目录存在"
|
||||
if [ -f "venv/bin/python" ]; then
|
||||
echo "虚拟环境Python可执行文件存在"
|
||||
venv/bin/python --version
|
||||
else
|
||||
echo "虚拟环境Python可执行文件不存在"
|
||||
fi
|
||||
else
|
||||
echo "虚拟环境目录不存在"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "5. 检查源代码文件:"
|
||||
for file in main.py build.spec requirements.txt; do
|
||||
if [ -f "$file" ]; then
|
||||
echo "✓ $file 存在"
|
||||
else
|
||||
echo "✗ $file 不存在"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "6. 检查app目录:"
|
||||
if [ -d "app" ]; then
|
||||
echo "✓ app目录存在"
|
||||
find app -name "*.py" | head -5 | while read file; do
|
||||
echo " ✓ $file"
|
||||
done
|
||||
else
|
||||
echo "✗ app目录不存在"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "7. 网络连接测试:"
|
||||
if ping -c 1 pypi.org &> /dev/null; then
|
||||
echo "✓ 可以访问PyPI"
|
||||
else
|
||||
echo "✗ 无法访问PyPI,可能需要配置代理"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 排查完成 ==="
|
||||
echo "如果发现问题,请根据上述输出进行修复"
|
||||
EOF
|
||||
|
||||
chmod +x "$PACKAGE_DIR/troubleshoot.sh"
|
||||
|
||||
# 创建压缩包
|
||||
echo "创建压缩包..."
|
||||
tar -czf "$PACKAGE_DIR.tar.gz" "$PACKAGE_DIR"
|
||||
|
||||
echo "=== 源代码包准备完成 ==="
|
||||
echo "生成的文件:"
|
||||
echo " $PACKAGE_DIR/ - 源代码目录"
|
||||
echo " $PACKAGE_DIR.tar.gz - 压缩包 ($(ls -lh "$PACKAGE_DIR.tar.gz" | awk '{print $5}'))"
|
||||
echo ""
|
||||
echo "修复内容:"
|
||||
echo " ✓ 添加了用户级安装脚本 install_user.sh"
|
||||
echo " ✓ 修复了 build.spec 依赖缺失问题"
|
||||
echo " ✓ 改进了打包脚本 build_linux.py"
|
||||
echo " ✓ 添加了问题排查脚本 troubleshoot.sh"
|
||||
echo " ✓ 增强了安装说明文档"
|
||||
echo ""
|
||||
echo "将压缩包上传到Linux服务器后,解压并运行:"
|
||||
echo " tar -xzf $PACKAGE_DIR.tar.gz"
|
||||
echo " cd $PACKAGE_DIR"
|
||||
echo " ./build_on_linux.sh"
|
||||
echo ""
|
||||
echo "如果遇到问题,可以先运行:"
|
||||
echo " ./troubleshoot.sh"
|
||||
EOF
|
||||
|
||||
chmod +x backend/prepare_linux_package_fixed.sh
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 快速打包脚本 - Linux环境
|
||||
|
||||
echo "=== 云盘后端快速打包脚本 ==="
|
||||
|
||||
# 检查Python环境
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "错误: 未找到Python3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Python版本: $(python3 --version)"
|
||||
|
||||
# 安装PyInstaller
|
||||
echo "安装PyInstaller..."
|
||||
pip3 install pyinstaller
|
||||
|
||||
# 安装项目依赖
|
||||
echo "安装项目依赖..."
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
# 运行打包
|
||||
echo "开始打包..."
|
||||
python3 build_linux.py
|
||||
|
||||
echo "=== 打包完成 ==="
|
||||
echo "部署包位置: ./deploy/"
|
||||
echo "请查看 ./deploy/README.md 了解部署说明"
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 快速数据库连接修复
|
||||
|
||||
echo "=== 快速数据库连接修复 ==="
|
||||
|
||||
# 检查.env文件并修复数据库URL
|
||||
if [ -f ".env" ]; then
|
||||
echo "修复 .env 文件中的数据库配置..."
|
||||
|
||||
# 备份原文件
|
||||
cp .env .env.backup.$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 更新数据库URL为外部数据库
|
||||
sed -i 's|DATABASE_URL=.*|DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db|' .env
|
||||
|
||||
echo "✓ 数据库配置已更新为外部数据库"
|
||||
else
|
||||
echo "创建 .env 文件..."
|
||||
cat > .env << EOF
|
||||
DATABASE_URL=mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db
|
||||
EOF
|
||||
echo "✓ .env 文件已创建"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "请重启应用以使配置生效"
|
||||
echo "重启命令: python main.py"
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
服务器端快速修复脚本
|
||||
修复PyInstaller打包时的database目录缺失问题
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def fix_build_spec():
|
||||
"""修复build.spec文件,处理可选数据文件"""
|
||||
spec_file = Path('build.spec')
|
||||
|
||||
if not spec_file.exists():
|
||||
print("错误: build.spec 文件不存在")
|
||||
return False
|
||||
|
||||
# 读取原文件内容
|
||||
with open(spec_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 查找并替换datas部分
|
||||
old_datas = '''# 需要包含的数据文件
|
||||
datas = [
|
||||
(str(ROOT_DIR / 'app'), 'app'), # 包含整个app目录
|
||||
('.env.example', '.'), # 包含环境配置示例文件
|
||||
('database', 'database'), # 包含数据库相关文件
|
||||
]'''
|
||||
|
||||
new_datas = '''# 需要包含的数据文件
|
||||
datas = [
|
||||
(str(ROOT_DIR / 'app'), 'app'), # 包含整个app目录
|
||||
('.env.example', '.'), # 包含环境配置示例文件
|
||||
]
|
||||
|
||||
# 可选数据文件(如果存在才包含)
|
||||
optional_files = [
|
||||
('database', 'database'), # 包含数据库相关文件
|
||||
]
|
||||
|
||||
# 添加可选数据文件
|
||||
for src, dst in optional_files:
|
||||
src_path = ROOT_DIR / src
|
||||
if src_path.exists():
|
||||
datas.append((src, dst))
|
||||
print(f"包含可选数据文件: {src}")
|
||||
else:
|
||||
print(f"跳过可选数据文件: {src} (不存在)")'''
|
||||
|
||||
if old_datas in content:
|
||||
content = content.replace(old_datas, new_datas)
|
||||
|
||||
# 写回文件
|
||||
with open(spec_file, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print("✓ 已修复 build.spec 文件")
|
||||
return True
|
||||
else:
|
||||
print("build.spec 文件已经包含修复或格式不匹配")
|
||||
return False
|
||||
|
||||
def create_database_dir():
|
||||
"""创建database目录(如果不存在)"""
|
||||
db_dir = Path('database')
|
||||
|
||||
if not db_dir.exists():
|
||||
db_dir.mkdir(exist_ok=True)
|
||||
|
||||
# 创建一个空的初始化文件
|
||||
init_file = db_dir / 'init' / '.gitkeep'
|
||||
init_file.parent.mkdir(exist_ok=True)
|
||||
init_file.touch()
|
||||
|
||||
# 创建一个说明文件
|
||||
readme_file = db_dir / 'README.md'
|
||||
with open(readme_file, 'w', encoding='utf-8') as f:
|
||||
f.write("""# Database目录
|
||||
|
||||
此目录包含数据库相关的初始化脚本和配置文件。
|
||||
|
||||
## 文件说明
|
||||
|
||||
- `init/`: 数据库初始化脚本目录
|
||||
- `create_files_table.py`: 创建文件表的脚本
|
||||
|
||||
## 注意
|
||||
|
||||
如果此目录为空,不会影响应用的正常运行。应用会自动创建所需的数据库表。
|
||||
""")
|
||||
|
||||
print("✓ 已创建 database 目录")
|
||||
return True
|
||||
else:
|
||||
print("✓ database 目录已存在")
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 服务器端快速修复脚本 ===")
|
||||
|
||||
# 修复build.spec文件
|
||||
print("1. 修复 build.spec 文件...")
|
||||
fix_build_spec()
|
||||
|
||||
# 创建database目录
|
||||
print("\n2. 检查 database 目录...")
|
||||
create_database_dir()
|
||||
|
||||
print("\n=== 修复完成 ===")
|
||||
print("现在可以重新运行打包:")
|
||||
print("python build_linux.py")
|
||||
|
||||
# 验证修复结果
|
||||
print("\n=== 验证修复结果 ===")
|
||||
|
||||
if Path('build.spec').exists():
|
||||
print("✓ build.spec 文件存在")
|
||||
else:
|
||||
print("✗ build.spec 文件不存在")
|
||||
|
||||
if Path('database').exists():
|
||||
print("✓ database 目录存在")
|
||||
files = list(Path('database').rglob('*'))
|
||||
print(f" 包含 {len(files)} 个文件/目录")
|
||||
else:
|
||||
print("✗ database 目录不存在")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,291 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
快速启动和恢复脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def check_python_version():
|
||||
"""检查Python版本"""
|
||||
if sys.version_info < (3, 8):
|
||||
print("错误: 需要Python 3.8或更高版本")
|
||||
return False
|
||||
print(f"✓ Python版本: {sys.version}")
|
||||
return True
|
||||
|
||||
def check_virtual_env():
|
||||
"""检查虚拟环境"""
|
||||
venv_path = Path('venv')
|
||||
if venv_path.exists():
|
||||
print("✓ 虚拟环境存在")
|
||||
return True
|
||||
else:
|
||||
print("✗ 虚拟环境不存在,正在创建...")
|
||||
try:
|
||||
subprocess.run([sys.executable, '-m', 'venv', 'venv'], check=True)
|
||||
print("✓ 虚拟环境创建成功")
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
print("✗ 虚拟环境创建失败")
|
||||
return False
|
||||
|
||||
def activate_virtual_env():
|
||||
"""激活虚拟环境"""
|
||||
if sys.platform == "win32":
|
||||
activate_script = Path('venv/Scripts/activate')
|
||||
else:
|
||||
activate_script = Path('venv/bin/activate')
|
||||
|
||||
if activate_script.exists():
|
||||
print("✓ 虚拟环境激活脚本存在")
|
||||
return True
|
||||
else:
|
||||
print("✗ 虚拟环境激活脚本不存在")
|
||||
return False
|
||||
|
||||
def install_dependencies():
|
||||
"""安装依赖"""
|
||||
print("安装依赖包...")
|
||||
|
||||
if sys.platform == "win32":
|
||||
pip_path = 'venv/Scripts/pip'
|
||||
python_path = 'venv/Scripts/python'
|
||||
else:
|
||||
pip_path = 'venv/bin/pip'
|
||||
python_path = 'venv/bin/python'
|
||||
|
||||
# 升级pip
|
||||
try:
|
||||
subprocess.run([python_path, '-m', 'pip', 'install', '--upgrade', 'pip'], check=True)
|
||||
print("✓ pip升级成功")
|
||||
except subprocess.CalledProcessError:
|
||||
print("✗ pip升级失败")
|
||||
|
||||
# 安装基础依赖
|
||||
basic_packages = [
|
||||
'fastapi==0.104.1',
|
||||
'uvicorn[standard]==0.24.0',
|
||||
'sqlalchemy==2.0.23',
|
||||
'pymysql==1.1.0',
|
||||
'python-jose[cryptography]==3.3.0',
|
||||
'passlib[bcrypt]==1.7.4',
|
||||
'python-multipart==0.0.6',
|
||||
'pydantic==2.5.0',
|
||||
'pydantic-settings==2.1.0',
|
||||
'httpx==0.25.2',
|
||||
'python-dotenv==1.0.0',
|
||||
'loguru>=0.7.0'
|
||||
]
|
||||
|
||||
try:
|
||||
for package in basic_packages:
|
||||
print(f"安装 {package}...")
|
||||
subprocess.run([pip_path, 'install', package], check=True)
|
||||
print("✓ 依赖安装成功")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"✗ 依赖安装失败: {e}")
|
||||
return False
|
||||
|
||||
def create_basic_app_structure():
|
||||
"""创建基本的app结构"""
|
||||
print("创建基本app结构...")
|
||||
|
||||
# 创建目录
|
||||
directories = [
|
||||
'app',
|
||||
'app/core',
|
||||
'app/api/v1/endpoints'
|
||||
]
|
||||
|
||||
for dir_path in directories:
|
||||
Path(dir_path).mkdir(parents=True, exist_ok=True)
|
||||
print(f"✓ 创建目录: {dir_path}")
|
||||
|
||||
# 创建__init__.py文件
|
||||
init_files = [
|
||||
('app/__init__.py', '"""云盘应用包"""\n'),
|
||||
('app/core/__init__.py', '"""核心模块包"""\n'),
|
||||
('app/api/__init__.py', '"""API模块包"""\n'),
|
||||
('app/api/v1/__init__.py', '"""API v1模块包"""\n'),
|
||||
('app/api/v1/endpoints/__init__.py', '"""API端点模块包"""\n')
|
||||
]
|
||||
|
||||
for file_path, content in init_files:
|
||||
full_path = Path(file_path)
|
||||
if not full_path.exists():
|
||||
full_path.write_text(content, encoding='utf-8')
|
||||
print(f"✓ 创建文件: {file_path}")
|
||||
|
||||
def create_essential_files():
|
||||
"""创建必要的文件"""
|
||||
print("创建必要的文件...")
|
||||
|
||||
# app/core/config.py
|
||||
config_content = '''from pydantic_settings import BaseSettings
|
||||
from typing import List
|
||||
|
||||
class Settings(BaseSettings):
|
||||
ENVIRONMENT: str = "development"
|
||||
DEBUG: bool = True
|
||||
DATABASE_URL: str = "mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db"
|
||||
REDIS_URL: str = "redis://localhost:6379"
|
||||
JWT_SECRET_KEY: str = "your-super-secret-jwt-key-change-in-production"
|
||||
JWT_ALGORITHM: str = "HS256"
|
||||
JWT_EXPIRE_MINUTES: int = 30
|
||||
ALLOWED_HOSTS: List[str] = ["*"]
|
||||
MAX_FILE_SIZE: int = 10 * 1024 * 1024
|
||||
UPLOAD_DIR: str = "uploads"
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
case_sensitive = True
|
||||
|
||||
settings = Settings()
|
||||
'''
|
||||
|
||||
Path('app/core/config.py').write_text(config_content, encoding='utf-8')
|
||||
print("✓ 创建 app/core/config.py")
|
||||
|
||||
# app/api/v1/endpoints/health.py
|
||||
health_content = '''from fastapi import APIRouter
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": datetime.utcnow(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@router.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs"
|
||||
}
|
||||
'''
|
||||
|
||||
Path('app/api/v1/endpoints/health.py').write_text(health_content, encoding='utf-8')
|
||||
print("✓ 创建 app/api/v1/endpoints/health.py")
|
||||
|
||||
def create_simple_main():
|
||||
"""创建简化的main.py"""
|
||||
print("创建简化的main.py...")
|
||||
|
||||
main_content = '''import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
print("启动云盘后端服务...")
|
||||
print("访问地址: http://localhost:8000")
|
||||
print("API文档: http://localhost:8000/docs")
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=False
|
||||
)
|
||||
'''
|
||||
|
||||
Path('main.py').write_text(main_content, encoding='utf-8')
|
||||
print("✓ 创建 main.py")
|
||||
|
||||
def start_project():
|
||||
"""启动项目"""
|
||||
print("启动项目...")
|
||||
|
||||
if sys.platform == "win32":
|
||||
python_path = 'venv/Scripts/python'
|
||||
else:
|
||||
python_path = 'venv/bin/python'
|
||||
|
||||
try:
|
||||
# 直接运行main.py
|
||||
subprocess.run([python_path, 'main.py'], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"启动失败: {e}")
|
||||
return False
|
||||
except KeyboardInterrupt:
|
||||
print("服务已停止")
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 云盘后端快速启动脚本 ===")
|
||||
|
||||
# 检查Python版本
|
||||
if not check_python_version():
|
||||
return
|
||||
|
||||
# 检查和创建虚拟环境
|
||||
if not check_virtual_env():
|
||||
print("虚拟环境创建失败")
|
||||
return
|
||||
|
||||
# 安装依赖
|
||||
if not install_dependencies():
|
||||
print("依赖安装失败")
|
||||
return
|
||||
|
||||
# 创建基本结构
|
||||
create_basic_app_structure()
|
||||
|
||||
# 创建必要文件
|
||||
create_essential_files()
|
||||
|
||||
# 创建简化main.py
|
||||
create_simple_main()
|
||||
|
||||
# 创建日志和上传目录
|
||||
Path('logs').mkdir(exist_ok=True)
|
||||
Path('uploads').mkdir(exist_ok=True)
|
||||
|
||||
print("\n=== 准备完成 ===")
|
||||
print("现在启动项目...")
|
||||
|
||||
# 启动项目
|
||||
start_project()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,491 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
重新创建完整的app目录结构和文件
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def create_app_directory():
|
||||
"""创建app目录结构"""
|
||||
print("=== 创建app目录结构 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
# 创建主要目录结构
|
||||
directories = [
|
||||
'app',
|
||||
'app/core',
|
||||
'app/api',
|
||||
'app/api/v1',
|
||||
'app/api/v1/endpoints',
|
||||
'app/models',
|
||||
'app/schemas',
|
||||
'app/services',
|
||||
'app/utils'
|
||||
]
|
||||
|
||||
for dir_path in directories:
|
||||
full_path = base_dir / dir_path
|
||||
full_path.mkdir(parents=True, exist_ok=True)
|
||||
print(f"✓ 创建目录: {dir_path}")
|
||||
|
||||
def create_init_files():
|
||||
"""创建__init__.py文件"""
|
||||
print("\n=== 创建__init__.py文件 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
# 各目录的__init__.py内容
|
||||
init_contents = {
|
||||
'app/__init__.py': '"""云盘应用包"""\n\n__version__ = "1.0.0"\n',
|
||||
'app/core/__init__.py': '"""核心模块包"""\n',
|
||||
'app/api/__init__.py': '"""API模块包"""\n',
|
||||
'app/api/v1/__init__.py': '"""API v1模块包"""\n',
|
||||
'app/api/v1/endpoints/__init__.py': '"""API端点模块包"""\n',
|
||||
'app/models/__init__.py': '"""数据模型包"""\n',
|
||||
'app/schemas/__init__.py': '"""Pydantic模式包"""\n',
|
||||
'app/services/__init__.py': '"""业务逻辑服务包"""\n',
|
||||
'app/utils/__init__.py': '"""工具函数包"""\n'
|
||||
}
|
||||
|
||||
for file_path, content in init_contents.items():
|
||||
full_path = base_dir / file_path
|
||||
if not full_path.exists():
|
||||
full_path.write_text(content, encoding='utf-8')
|
||||
print(f"✓ 创建文件: {file_path}")
|
||||
|
||||
def create_core_files():
|
||||
"""创建核心文件"""
|
||||
print("\n=== 创建核心文件 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
# app/core/config.py
|
||||
config_content = '''from pydantic_settings import BaseSettings
|
||||
from typing import List
|
||||
import os
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# 基础配置
|
||||
ENVIRONMENT: str = "development"
|
||||
DEBUG: bool = True
|
||||
|
||||
# 数据库配置
|
||||
DATABASE_URL: str = "mysql+pymysql://mytest_db:mytest_db@101.126.85.76:3306/mytest_db"
|
||||
|
||||
# Redis配置
|
||||
REDIS_URL: str = "redis://localhost:6379"
|
||||
|
||||
# JWT配置
|
||||
JWT_SECRET_KEY: str = "your-super-secret-jwt-key-change-in-production"
|
||||
JWT_ALGORITHM: str = "HS256"
|
||||
JWT_EXPIRE_MINUTES: int = 30
|
||||
JWT_REFRESH_EXPIRE_DAYS: int = 7
|
||||
|
||||
# CORS配置
|
||||
ALLOWED_HOSTS: List[str] = [
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3001",
|
||||
"http://localhost:3002",
|
||||
"http://localhost:3003",
|
||||
"http://localhost:3004",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://127.0.0.1:3001",
|
||||
"http://127.0.0.1:3002",
|
||||
"http://127.0.0.1:3003",
|
||||
"http://127.0.0.1:3004",
|
||||
"http://172.16.16.89:3000",
|
||||
"http://172.16.16.89:3001",
|
||||
"http://172.16.16.89:3002",
|
||||
"http://172.16.16.89:3003",
|
||||
"http://172.16.16.89:3004",
|
||||
"*"
|
||||
]
|
||||
|
||||
# 文件上传配置
|
||||
MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 10MB
|
||||
UPLOAD_DIR: str = "uploads"
|
||||
ALLOWED_EXTENSIONS: List[str] = [
|
||||
# 图片
|
||||
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg",
|
||||
# 文档
|
||||
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
|
||||
".txt", ".rtf", ".csv",
|
||||
# 压缩文件
|
||||
".zip", ".rar", ".7z", ".tar", ".gz",
|
||||
# 音频
|
||||
".mp3", ".wav", ".flac", ".aac", ".ogg",
|
||||
# 视频
|
||||
".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv",
|
||||
# 代码文件
|
||||
".py", ".js", ".html", ".css", ".json", ".xml", ".yaml", ".yml",
|
||||
".java", ".cpp", ".c", ".h", ".cs", ".php", ".rb", ".go",
|
||||
".sql", ".sh", ".bat", ".ps1", ".md", ".log"
|
||||
]
|
||||
|
||||
# 安全配置
|
||||
BCRYPT_ROUNDS: int = 12
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
case_sensitive = True
|
||||
|
||||
settings = Settings()
|
||||
'''
|
||||
|
||||
config_file = base_dir / 'app' / 'core' / 'config.py'
|
||||
config_file.write_text(config_content, encoding='utf-8')
|
||||
print("✓ 创建 app/core/config.py")
|
||||
|
||||
# app/core/database.py
|
||||
database_content = '''from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from app.core.config import settings
|
||||
|
||||
# 创建数据库引擎
|
||||
engine = create_engine(
|
||||
settings.DATABASE_URL,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=300,
|
||||
)
|
||||
|
||||
# 创建会话工厂
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# 创建Base类
|
||||
Base = declarative_base()
|
||||
|
||||
def get_db():
|
||||
"""获取数据库会话"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
'''
|
||||
|
||||
database_file = base_dir / 'app' / 'core' / 'database.py'
|
||||
database_file.write_text(database_content, encoding='utf-8')
|
||||
print("✓ 创建 app/core/database.py")
|
||||
|
||||
# app/core/security.py
|
||||
security_content = '''from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from jose import JWTError, jwt
|
||||
from passlib.context import CryptContext
|
||||
from app.core.config import settings
|
||||
|
||||
# 密码加密上下文
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""验证密码"""
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
"""获取密码哈希"""
|
||||
return pwd_context.hash(password)
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||
"""创建访问令牌"""
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
|
||||
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
def verify_token(token: str):
|
||||
"""验证令牌"""
|
||||
try:
|
||||
payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
return None
|
||||
'''
|
||||
|
||||
security_file = base_dir / 'app' / 'core' / 'security.py'
|
||||
security_file.write_text(security_content, encoding='utf-8')
|
||||
print("✓ 创建 app/core/security.py")
|
||||
|
||||
def create_api_files():
|
||||
"""创建API文件"""
|
||||
print("\n=== 创建API文件 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
# app/api/v1/endpoints/health.py
|
||||
health_content = '''from fastapi import APIRouter, status
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/health", status_code=status.HTTP_200_OK)
|
||||
async def health_check():
|
||||
"""健康检查端点"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": datetime.utcnow(),
|
||||
"version": "1.0.0",
|
||||
"message": "云盘后端服务运行正常"
|
||||
}
|
||||
|
||||
@router.get("/")
|
||||
async def root():
|
||||
"""根端点"""
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health"
|
||||
}
|
||||
'''
|
||||
|
||||
health_file = base_dir / 'app' / 'api' / 'v1' / 'endpoints' / 'health.py'
|
||||
health_file.write_text(health_content, encoding='utf-8')
|
||||
print("✓ 创建 app/api/v1/endpoints/health.py")
|
||||
|
||||
# app/api/v1/endpoints/auth.py
|
||||
auth_content = '''from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
from datetime import timedelta
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.security import verify_password, create_access_token
|
||||
from app.core.config import settings
|
||||
from app.schemas.user import UserCreate, UserResponse, Token
|
||||
|
||||
router = APIRouter()
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/auth/token")
|
||||
|
||||
@router.post("/register", response_model=UserResponse)
|
||||
async def register(user_data: UserCreate, db: Session = Depends(get_db)):
|
||||
"""用户注册"""
|
||||
# TODO: 实现用户注册逻辑
|
||||
return {"message": "注册功能待实现"}
|
||||
|
||||
@router.post("/token", response_model=Token)
|
||||
async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
|
||||
"""用户登录"""
|
||||
# TODO: 实现用户登录逻辑
|
||||
return {
|
||||
"access_token": "dummy_token",
|
||||
"token_type": "bearer",
|
||||
"expires_in": settings.JWT_EXPIRE_MINUTES * 60
|
||||
}
|
||||
|
||||
@router.get("/me", response_model=UserResponse)
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
|
||||
"""获取当前用户信息"""
|
||||
# TODO: 实现获取当前用户逻辑
|
||||
return {"message": "用户信息功能待实现"}
|
||||
'''
|
||||
|
||||
auth_file = base_dir / 'app' / 'api' / 'v1' / 'endpoints' / 'auth.py'
|
||||
auth_file.write_text(auth_content, encoding='utf-8')
|
||||
print("✓ 创建 app/api/v1/endpoints/auth.py")
|
||||
|
||||
# app/api/v1/endpoints/files.py
|
||||
files_content = '''from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.schemas.file import FileResponse, FileUploadResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/upload", response_model=FileUploadResponse)
|
||||
async def upload_file(
|
||||
file: UploadFile = File(...),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""上传文件"""
|
||||
# TODO: 实现文件上传逻辑
|
||||
return {
|
||||
"message": "文件上传功能待实现",
|
||||
"filename": file.filename,
|
||||
"size": 0
|
||||
}
|
||||
|
||||
@router.get("/list", response_model=List[FileResponse])
|
||||
async def list_files(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取文件列表"""
|
||||
# TODO: 实现文件列表逻辑
|
||||
return []
|
||||
|
||||
@router.get("/{file_id}", response_model=FileResponse)
|
||||
async def get_file_info(file_id: int, db: Session = Depends(get_db)):
|
||||
"""获取文件信息"""
|
||||
# TODO: 实现获取文件信息逻辑
|
||||
return {"message": "文件信息功能待实现"}
|
||||
|
||||
@router.delete("/{file_id}")
|
||||
async def delete_file(file_id: int, db: Session = Depends(get_db)):
|
||||
"""删除文件"""
|
||||
# TODO: 实现文件删除逻辑
|
||||
return {"message": "文件删除功能待实现"}
|
||||
'''
|
||||
|
||||
files_file = base_dir / 'app' / 'api' / 'v1' / 'endpoints' / 'files.py'
|
||||
files_file.write_text(files_content, encoding='utf-8')
|
||||
print("✓ 创建 app/api/v1/endpoints/files.py")
|
||||
|
||||
def create_schema_files():
|
||||
"""创建Pydantic模式文件"""
|
||||
print("\n=== 创建模式文件 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
# app/schemas/user.py
|
||||
user_schema_content = '''from pydantic import BaseModel, EmailStr
|
||||
from typing import Optional
|
||||
|
||||
class UserBase(BaseModel):
|
||||
username: str
|
||||
email: EmailStr
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str
|
||||
confirm_password: str
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
username: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
|
||||
class UserResponse(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
created_at: str
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
expires_in: int
|
||||
'''
|
||||
|
||||
user_schema_file = base_dir / 'app' / 'schemas' / 'user.py'
|
||||
user_schema_file.write_text(user_schema_content, encoding='utf-8')
|
||||
print("✓ 创建 app/schemas/user.py")
|
||||
|
||||
# app/schemas/file.py
|
||||
file_schema_content = '''from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
class FileBase(BaseModel):
|
||||
filename: str
|
||||
original_filename: str
|
||||
file_size: int
|
||||
content_type: str
|
||||
|
||||
class FileCreate(FileBase):
|
||||
pass
|
||||
|
||||
class FileUpdate(BaseModel):
|
||||
filename: Optional[str] = None
|
||||
|
||||
class FileResponse(FileBase):
|
||||
id: int
|
||||
user_id: int
|
||||
file_path: str
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class FileUploadResponse(BaseModel):
|
||||
message: str
|
||||
filename: str
|
||||
size: int
|
||||
file_id: Optional[int] = None
|
||||
'''
|
||||
|
||||
file_schema_file = base_dir / 'app' / 'schemas' / 'file.py'
|
||||
file_schema_file.write_text(file_schema_content, encoding='utf-8')
|
||||
print("✓ 创建 app/schemas/file.py")
|
||||
|
||||
def verify_structure():
|
||||
"""验证项目结构"""
|
||||
print("\n=== 验证项目结构 ===")
|
||||
|
||||
base_dir = Path('.')
|
||||
|
||||
required_files = [
|
||||
'app/__init__.py',
|
||||
'app/core/__init__.py',
|
||||
'app/core/config.py',
|
||||
'app/core/database.py',
|
||||
'app/core/security.py',
|
||||
'app/api/__init__.py',
|
||||
'app/api/v1/__init__.py',
|
||||
'app/api/v1/endpoints/__init__.py',
|
||||
'app/api/v1/endpoints/health.py',
|
||||
'app/api/v1/endpoints/auth.py',
|
||||
'app/api/v1/endpoints/files.py',
|
||||
'app/schemas/__init__.py',
|
||||
'app/schemas/user.py',
|
||||
'app/schemas/file.py'
|
||||
]
|
||||
|
||||
all_exist = True
|
||||
for file_path in required_files:
|
||||
full_path = base_dir / file_path
|
||||
if full_path.exists():
|
||||
print(f"✓ {file_path}")
|
||||
else:
|
||||
print(f"✗ {file_path}")
|
||||
all_exist = False
|
||||
|
||||
if all_exist:
|
||||
print("\n🎉 所有文件创建成功!")
|
||||
print("现在可以运行: python main.py")
|
||||
else:
|
||||
print("\n⚠️ 部分文件创建失败,请检查错误信息")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 重新创建app目录结构 ===")
|
||||
|
||||
# 创建目录结构
|
||||
create_app_directory()
|
||||
|
||||
# 创建__init__.py文件
|
||||
create_init_files()
|
||||
|
||||
# 创建核心文件
|
||||
create_core_files()
|
||||
|
||||
# 创建API文件
|
||||
create_api_files()
|
||||
|
||||
# 创建模式文件
|
||||
create_schema_files()
|
||||
|
||||
# 验证结构
|
||||
verify_structure()
|
||||
|
||||
print("\n=== 恢复完成 ===")
|
||||
print("app目录结构和所有必要文件已重新创建")
|
||||
print("下一步:")
|
||||
print("1. 激活虚拟环境: source venv/bin/activate")
|
||||
print("2. 安装依赖: pip install -r requirements.txt")
|
||||
print("3. 启动服务: python main.py")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,271 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 不依赖loguru的启动脚本
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
# 配置标准日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.info("Starting Cloud Drive Application Server...")
|
||||
|
||||
# 导入FastAPI相关
|
||||
try:
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
logger.info("FastAPI dependencies available")
|
||||
except ImportError as e:
|
||||
logger.error(f"FastAPI import failed: {e}")
|
||||
logger.error("Please install dependencies: pip install fastapi uvicorn")
|
||||
sys.exit(1)
|
||||
|
||||
# 尝试导入app模块
|
||||
try:
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
APP_AVAILABLE = True
|
||||
logger.info("Full app module available")
|
||||
except ImportError as e:
|
||||
logger.warning(f"App module import failed: {e}")
|
||||
logger.info("Using simplified mode")
|
||||
APP_AVAILABLE = False
|
||||
|
||||
def create_app():
|
||||
"""创建FastAPI应用"""
|
||||
if APP_AVAILABLE:
|
||||
# 使用完整的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
try:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"CORS configuration failed: {e}")
|
||||
# 使用默认配置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
try:
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
except Exception as e:
|
||||
logger.warning(f"Router inclusion failed: {e}")
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.1",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health"
|
||||
}
|
||||
|
||||
# 添加缺失的端点
|
||||
@app.get("/health")
|
||||
async def health_endpoint():
|
||||
import time
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"status": "healthy",
|
||||
"service": "cloud-drive-api",
|
||||
"environment": "development",
|
||||
"timestamp": int(time.time())
|
||||
},
|
||||
"message": "API服务运行正常"
|
||||
}
|
||||
|
||||
@app.get("/test")
|
||||
async def test_endpoint():
|
||||
return {
|
||||
"message": "测试端点正常工作",
|
||||
"server": "port 8080",
|
||||
"status": "ok"
|
||||
}
|
||||
|
||||
@app.get("/info")
|
||||
async def info_endpoint():
|
||||
return {
|
||||
"mode": "full",
|
||||
"python_version": str(sys.version),
|
||||
"status": "running",
|
||||
"app_type": "FastAPI"
|
||||
}
|
||||
|
||||
return app
|
||||
else:
|
||||
# 创建简化版本的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API (简化版)",
|
||||
description="云存储Web应用后端API - 简化版本",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API (简化版)",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/health",
|
||||
"mode": "simplified"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "服务运行正常",
|
||||
"mode": "simplified"
|
||||
}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def api_health():
|
||||
import time
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@app.get("/test")
|
||||
async def test_endpoint():
|
||||
return {
|
||||
"message": "测试端点正常工作",
|
||||
"server": "port 8080",
|
||||
"status": "ok"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_endpoint():
|
||||
import time
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"status": "healthy",
|
||||
"service": "cloud-drive-api",
|
||||
"environment": "development",
|
||||
"timestamp": int(time.time())
|
||||
},
|
||||
"message": "API服务运行正常"
|
||||
}
|
||||
|
||||
@app.get("/info")
|
||||
async def info_endpoint():
|
||||
return {
|
||||
"mode": "simplified",
|
||||
"python_version": str(sys.version),
|
||||
"status": "running",
|
||||
"app_type": "FastAPI"
|
||||
}
|
||||
|
||||
return app
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
logger.info("Initializing Cloud Drive Application...")
|
||||
|
||||
# 创建必要目录
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
os.makedirs("uploads", exist_ok=True)
|
||||
logger.info("Created necessary directories")
|
||||
|
||||
# 创建FastAPI应用
|
||||
app = create_app()
|
||||
logger.info("FastAPI application created")
|
||||
|
||||
# 获取本机IP
|
||||
try:
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
local_ip = "127.0.0.1"
|
||||
logger.warning("Could not determine local IP, using 127.0.0.1")
|
||||
|
||||
# 显示启动信息
|
||||
logger.info("=" * 50)
|
||||
logger.info(f"Local access:")
|
||||
logger.info(f" Root path: http://localhost:8080")
|
||||
logger.info(f" API docs: http://localhost:8080/docs")
|
||||
logger.info(f" ReDoc: http://localhost:8080/redoc")
|
||||
logger.info(f" Health check: http://localhost:8080/api/v1/health")
|
||||
logger.info("")
|
||||
logger.info(f"Network access:")
|
||||
logger.info(f" Root path: http://{local_ip}:8080")
|
||||
logger.info(f" API docs: http://{local_ip}:8080/docs")
|
||||
logger.info("")
|
||||
logger.info("Service information:")
|
||||
logger.info(f" Python version: {sys.version}")
|
||||
logger.info(f" Working directory: {os.getcwd()}")
|
||||
logger.info(f" Mode: {'Full' if APP_AVAILABLE else 'Simplified'}")
|
||||
logger.info("=" * 50)
|
||||
logger.info("Press Ctrl+C to stop service")
|
||||
|
||||
# 启动服务器
|
||||
try:
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Server stopped by user")
|
||||
except Exception as e:
|
||||
logger.error(f"Startup failed: {e}")
|
||||
logger.info("Possible solutions:")
|
||||
logger.info("1. Check if port 8080 is occupied")
|
||||
logger.info("2. Ensure dependencies are installed: pip install fastapi uvicorn")
|
||||
logger.info("3. Check firewall settings")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 简单的Docker镜像构建脚本
|
||||
# 当无法访问Docker Hub时使用
|
||||
|
||||
echo "=== 云盘应用 Docker 镜像构建工具 ==="
|
||||
|
||||
# 检查可执行文件
|
||||
if [ ! -f "dist/cloud-drive-server.exe" ]; then
|
||||
echo "错误: 未找到可执行文件"
|
||||
echo "请先运行: python package-app.py"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建临时目录
|
||||
TEMP_DIR="temp-docker"
|
||||
rm -rf $TEMP_DIR
|
||||
mkdir -p $TEMP_DIR
|
||||
|
||||
echo "正在准备Docker镜像内容..."
|
||||
|
||||
# 复制可执行文件
|
||||
cp dist/cloud-drive-server.exe $TEMP_DIR/
|
||||
|
||||
# 创建运行脚本
|
||||
cat > $TEMP_DIR/start.sh << 'EOF'
|
||||
#!/bin/sh
|
||||
# 设置时区
|
||||
export TZ=Asia/Shanghai
|
||||
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
# 创建必要目录
|
||||
mkdir -p /app/uploads /app/logs
|
||||
|
||||
# 启动应用
|
||||
exec ./cloud-drive-server.exe
|
||||
EOF
|
||||
|
||||
chmod +x $TEMP_DIR/start.sh
|
||||
|
||||
# 创建简化的Dockerfile
|
||||
cat > $TEMP_DIR/Dockerfile << 'EOF'
|
||||
# 使用scratch基础镜像(无依赖)
|
||||
FROM scratch
|
||||
|
||||
# 复制可执行文件和脚本
|
||||
COPY cloud-drive-server.exe /
|
||||
COPY start.sh /
|
||||
|
||||
# 设置执行权限
|
||||
CMD ["/start.sh"]
|
||||
EOF
|
||||
|
||||
echo "Docker镜像内容准备完成"
|
||||
echo "临时目录: $TEMP_DIR"
|
||||
|
||||
# 如果可以使用Docker
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "正在构建Docker镜像..."
|
||||
cd $TEMP_DIR
|
||||
|
||||
# 尝试构建
|
||||
if docker build -t cloud-drive-backend:simple . 2>/dev/null; then
|
||||
echo "OK Docker镜像构建成功"
|
||||
echo "镜像名称: cloud-drive-backend:simple"
|
||||
echo ""
|
||||
echo "运行命令:"
|
||||
echo " docker run -d -p 8002:8002 --name cloud-drive-backend cloud-drive-backend:simple"
|
||||
else
|
||||
echo "Docker镜像构建失败,可能是网络问题"
|
||||
echo "请检查Docker网络配置或稍后重试"
|
||||
fi
|
||||
|
||||
cd ..
|
||||
else
|
||||
echo "未找到Docker命令"
|
||||
fi
|
||||
|
||||
echo "=== 构建完成 ==="
|
||||
@@ -1,168 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
简化版文件存储检查脚本
|
||||
"""
|
||||
|
||||
import mysql.connector
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
def check_files_storage():
|
||||
"""检查文件存储情况"""
|
||||
|
||||
print("=== 文件存储情况检查 ===")
|
||||
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 查询所有文件
|
||||
cursor.execute("""
|
||||
SELECT id, user_id, original_filename, filename, file_path, file_size,
|
||||
file_hash, mime_type, created_at
|
||||
FROM files
|
||||
ORDER BY created_at DESC
|
||||
""")
|
||||
|
||||
db_files = cursor.fetchall()
|
||||
print(f"数据库中的文件记录数: {len(db_files)}")
|
||||
print()
|
||||
|
||||
print("=== 数据库中的文件记录 ===")
|
||||
for file in db_files:
|
||||
(id, user_id, original_filename, filename, file_path,
|
||||
file_size, file_hash, mime_type, created_at) = file
|
||||
|
||||
print(f"ID: {id}")
|
||||
print(f" 原始文件名: {original_filename}")
|
||||
print(f" 存储文件名: {filename}")
|
||||
print(f" 文件大小: {file_size} bytes")
|
||||
print(f" MIME类型: {mime_type}")
|
||||
print(f" 创建时间: {created_at}")
|
||||
print("-" * 40)
|
||||
|
||||
# 检查实际文件存在情况
|
||||
print("\n=== 文件存在性检查 ===")
|
||||
existing_count = 0
|
||||
for file in db_files:
|
||||
(id, user_id, original_filename, filename, file_path,
|
||||
file_size, file_hash, mime_type, created_at) = file
|
||||
|
||||
full_path = os.path.join("uploads", filename)
|
||||
if os.path.exists(full_path):
|
||||
existing_count += 1
|
||||
print(f"[存在] ID {id}: {original_filename}")
|
||||
else:
|
||||
print(f"[缺失] ID {id}: {original_filename}")
|
||||
|
||||
print(f"\n实际存在的文件数: {existing_count}")
|
||||
print(f"缺失的文件数: {len(db_files) - existing_count}")
|
||||
|
||||
# 检查uploads目录详情
|
||||
print("\n=== uploads目录详情 ===")
|
||||
uploads_dir = "uploads"
|
||||
if os.path.exists(uploads_dir):
|
||||
files = os.listdir(uploads_dir)
|
||||
print(f"uploads目录中的文件数: {len(files)}")
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(uploads_dir, file)
|
||||
file_size = os.path.getsize(file_path)
|
||||
print(f"文件: {file}, 大小: {file_size} bytes")
|
||||
else:
|
||||
print("uploads目录不存在")
|
||||
|
||||
except Exception as e:
|
||||
print(f"检查出错: {e}")
|
||||
finally:
|
||||
if 'conn' in locals() and conn.is_connected():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
def check_file_integrity():
|
||||
"""检查文件完整性"""
|
||||
|
||||
print("\n=== 文件完整性检查 ===")
|
||||
|
||||
try:
|
||||
conn = mysql.connector.connect(
|
||||
host="101.126.85.76",
|
||||
user="mytest_db",
|
||||
password="mytest_db",
|
||||
database="mytest_db"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT id, filename, file_hash, file_size FROM files")
|
||||
db_files = cursor.fetchall()
|
||||
|
||||
integrity_ok = True
|
||||
|
||||
for (id, filename, expected_hash, expected_size) in db_files:
|
||||
full_path = os.path.join("uploads", filename)
|
||||
|
||||
if os.path.exists(full_path):
|
||||
actual_size = os.path.getsize(full_path)
|
||||
if actual_size != expected_size:
|
||||
print(f"ID {id}: 文件大小不匹配 (期望: {expected_size}, 实际: {actual_size})")
|
||||
integrity_ok = False
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(full_path, 'rb') as f:
|
||||
content = f.read()
|
||||
actual_hash = hashlib.sha256(content).hexdigest()
|
||||
|
||||
if actual_hash != expected_hash:
|
||||
print(f"ID {id}: 文件哈希不匹配")
|
||||
print(f" 期望: {expected_hash}")
|
||||
print(f" 实际: {actual_hash}")
|
||||
integrity_ok = False
|
||||
else:
|
||||
print(f"ID {id}: 完整性检查通过")
|
||||
except Exception as e:
|
||||
print(f"ID {id}: 无法计算哈希 - {e}")
|
||||
integrity_ok = False
|
||||
else:
|
||||
print(f"ID {id}: 文件不存在")
|
||||
integrity_ok = False
|
||||
|
||||
if integrity_ok:
|
||||
print("所有文件完整性检查通过!")
|
||||
else:
|
||||
print("发现文件完整性问题!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"完整性检查出错: {e}")
|
||||
finally:
|
||||
if 'conn' in locals() and conn.is_connected():
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_files_storage()
|
||||
check_file_integrity()
|
||||
|
||||
print("\n=== 文件存储架构总结 ===")
|
||||
print("1. 数据库(files表): 存储文件元数据")
|
||||
print(" - 文件ID、用户ID、原始文件名")
|
||||
print(" - 存储文件名(UUID格式)")
|
||||
print(" - 文件大小、MIME类型")
|
||||
print(" - SHA-256哈希值")
|
||||
print(" - 创建时间等")
|
||||
print()
|
||||
print("2. 文件系统(uploads目录): 存储实际文件")
|
||||
print(" - 文件使用UUID命名确保唯一性")
|
||||
print(" - 文件内容与数据库记录一一对应")
|
||||
print(" - 通过file_hash验证完整性")
|
||||
print()
|
||||
print("3. 回答您的问题:")
|
||||
print(" 是的,数据库中存储的是文件元数据,")
|
||||
print(" 实际文件内容存储在服务器的uploads目录中。")
|
||||
print(" 两者通过file_hash保持关联和完整性验证。")
|
||||
@@ -1,226 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
简化版文件哈希演示脚本
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import requests
|
||||
import os
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
USER_ID = 8
|
||||
|
||||
def calculate_sha256_hash(file_content: bytes) -> str:
|
||||
"""计算文件的SHA-256哈希值"""
|
||||
return hashlib.sha256(file_content).hexdigest()
|
||||
|
||||
def upload_and_demo_hash():
|
||||
"""上传文件并演示哈希功能"""
|
||||
|
||||
# 创建测试文件内容
|
||||
original_content = "Hello World! 这是演示文件哈希的测试内容。"
|
||||
|
||||
print("=== 文件哈希演示 ===")
|
||||
print("原始文件内容:")
|
||||
print(f"'{original_content}'")
|
||||
print(f"文件大小: {len(original_content.encode('utf-8'))} bytes")
|
||||
print()
|
||||
|
||||
# 计算哈希值
|
||||
file_hash = calculate_sha256_hash(original_content.encode('utf-8'))
|
||||
print("计算的SHA-256哈希值:")
|
||||
print(file_hash)
|
||||
print()
|
||||
|
||||
# 上传文件
|
||||
try:
|
||||
files = {
|
||||
"file": ("hash_demo.txt", original_content.encode('utf-8'), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"description": "哈希演示文件",
|
||||
"tags": "demo,hash",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
server_hash = file_info["file_hash"]
|
||||
file_id = file_info["id"]
|
||||
|
||||
print("文件上传成功!")
|
||||
print(f"文件ID: {file_id}")
|
||||
print(f"服务器哈希值: {server_hash}")
|
||||
print()
|
||||
|
||||
# 验证哈希值一致性
|
||||
if file_hash == server_hash:
|
||||
print("哈希值验证通过! 客户端和服务器计算结果一致")
|
||||
else:
|
||||
print("哈希值验证失败!")
|
||||
print(f"客户端: {file_hash}")
|
||||
print(f"服务器: {server_hash}")
|
||||
|
||||
return file_id, original_content, file_hash, file_info['filename']
|
||||
else:
|
||||
print(f"上传失败: {response.text}")
|
||||
return None, None, None, None
|
||||
|
||||
except Exception as e:
|
||||
print(f"上传出错: {e}")
|
||||
return None, None, None, None
|
||||
|
||||
def verify_download_integrity(file_id, original_content, original_hash, filename):
|
||||
"""验证下载文件的完整性"""
|
||||
|
||||
print("\n=== 文件下载和完整性验证 ===")
|
||||
|
||||
try:
|
||||
# 下载文件
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/download",
|
||||
json=data
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
downloaded_content = response.content.decode('utf-8')
|
||||
|
||||
print("下载的文件内容:")
|
||||
print(f"'{downloaded_content}'")
|
||||
print()
|
||||
|
||||
# 验证内容完整性
|
||||
if downloaded_content == original_content:
|
||||
print("内容完整性验证通过!")
|
||||
else:
|
||||
print("内容完整性验证失败!")
|
||||
|
||||
# 计算下载文件的哈希值
|
||||
downloaded_hash = calculate_sha256_hash(downloaded_content.encode('utf-8'))
|
||||
print("下载文件的哈希值:")
|
||||
print(downloaded_hash)
|
||||
print()
|
||||
|
||||
# 验证哈希值
|
||||
if downloaded_hash == original_hash:
|
||||
print("下载文件哈希验证通过! 文件完整性得到保证")
|
||||
else:
|
||||
print("下载文件哈希验证失败! 文件可能已损坏")
|
||||
print(f"原始哈希: {original_hash}")
|
||||
print(f"下载哈希: {downloaded_hash}")
|
||||
|
||||
else:
|
||||
print(f"下载失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"下载过程出错: {e}")
|
||||
|
||||
def show_server_file_info(filename):
|
||||
"""显示服务器上文件的信息"""
|
||||
|
||||
print("\n=== 服务器文件信息 ===")
|
||||
|
||||
# 检查uploads目录
|
||||
upload_path = os.path.join("uploads", filename)
|
||||
if os.path.exists(upload_path):
|
||||
file_size = os.path.getsize(upload_path)
|
||||
print(f"文件路径: {upload_path}")
|
||||
print(f"文件大小: {file_size} bytes")
|
||||
|
||||
# 计算文件哈希
|
||||
with open(upload_path, 'rb') as f:
|
||||
content = f.read()
|
||||
file_hash = calculate_sha256_hash(content)
|
||||
print(f"文件SHA-256哈希: {file_hash}")
|
||||
|
||||
# 显示文件内容
|
||||
with open(upload_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
print(f"文件内容: '{content}'")
|
||||
else:
|
||||
print(f"文件不存在: {upload_path}")
|
||||
|
||||
def demonstrate_hash_properties():
|
||||
"""演示哈希的重要特性"""
|
||||
|
||||
print("\n=== 哈希特性演示 ===")
|
||||
|
||||
# 特性1: 相同输入产生相同哈希
|
||||
text1 = "Hello World"
|
||||
text2 = "Hello World"
|
||||
hash1 = calculate_sha256_hash(text1.encode())
|
||||
hash2 = calculate_sha256_hash(text2.encode())
|
||||
|
||||
print("特性1: 相同输入产生相同哈希")
|
||||
print(f"文本1: '{text1}' -> {hash1}")
|
||||
print(f"文本2: '{text2}' -> {hash2}")
|
||||
print(f"哈希值相同: {hash1 == hash2}")
|
||||
print()
|
||||
|
||||
# 特性2: 微小变化导致完全不同的哈希
|
||||
text3 = "Hello World"
|
||||
text4 = "Hello World!" # 只多了一个感叹号
|
||||
hash3 = calculate_sha256_hash(text3.encode())
|
||||
hash4 = calculate_sha256_hash(text4.encode())
|
||||
|
||||
print("特性2: 微小变化导致完全不同的哈希")
|
||||
print(f"文本3: '{text3}' -> {hash3}")
|
||||
print(f"文本4: '{text4}' -> {hash4}")
|
||||
print(f"哈希值不同: {hash3 != hash4}")
|
||||
print()
|
||||
|
||||
# 特性3: 不可逆性
|
||||
sample_hash = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
|
||||
print("特性3: 哈希是单向的,无法从哈希反推原始内容")
|
||||
print(f"示例哈希: {sample_hash}")
|
||||
print("无法从这个哈希值推断出原始文本内容")
|
||||
print()
|
||||
|
||||
def main():
|
||||
"""主演示函数"""
|
||||
|
||||
# 演示哈希特性
|
||||
demonstrate_hash_properties()
|
||||
|
||||
# 上传并演示哈希功能
|
||||
file_id, original_content, original_hash, filename = upload_and_demo_hash()
|
||||
|
||||
if file_id:
|
||||
# 验证下载完整性
|
||||
verify_download_integrity(file_id, original_content, original_hash, filename)
|
||||
|
||||
# 显示服务器文件信息
|
||||
show_server_file_info(filename)
|
||||
|
||||
print("\n=== 总结 ===")
|
||||
print("file_hash 的作用:")
|
||||
print("1. 完整性验证 - 确保文件在传输存储过程中未损坏")
|
||||
print("2. 文件去重 - 相同内容只存储一份,节省空间")
|
||||
print("3. 安全检查 - 防止恶意文件和内容篡改")
|
||||
print("4. 快速比较 - 通过哈希值快速判断文件是否相同")
|
||||
print()
|
||||
print("如何还原文件:")
|
||||
print("1. 通过API下载: POST /api/v1/files/download")
|
||||
print("2. 直接从服务器目录读取: backend/uploads/[filename]")
|
||||
print("3. 验证文件完整性: 计算SHA-256哈希并与数据库中的file_hash比较")
|
||||
|
||||
else:
|
||||
print("演示失败,无法上传测试文件")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 简单的HTTP服务器用于端口测试
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
|
||||
class SimpleHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
print(f"收到请求: {self.path}")
|
||||
|
||||
# 设置响应头
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
|
||||
# 根据路径返回不同响应
|
||||
if self.path == '/':
|
||||
response = {
|
||||
"message": "简单HTTP服务器运行正常",
|
||||
"port": 8080,
|
||||
"status": "ok"
|
||||
}
|
||||
elif self.path == '/health':
|
||||
response = {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"server": "simple-http-server"
|
||||
}
|
||||
elif self.path == '/api/v1/health':
|
||||
response = {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0",
|
||||
"server": "simple-http-server"
|
||||
}
|
||||
elif self.path == '/test':
|
||||
response = {
|
||||
"test": "ok",
|
||||
"message": "测试端点正常工作"
|
||||
}
|
||||
else:
|
||||
response = {
|
||||
"error": "Not Found",
|
||||
"message": "路径不存在",
|
||||
"path": self.path
|
||||
}
|
||||
|
||||
self.wfile.write(json.dumps(response, ensure_ascii=False).encode('utf-8'))
|
||||
|
||||
def do_OPTIONS(self):
|
||||
# 处理CORS预检请求
|
||||
self.send_response(200)
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
||||
self.end_headers()
|
||||
|
||||
def check_port_available(port):
|
||||
"""检查端口是否可用"""
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('0.0.0.0', port))
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
def main():
|
||||
port = 8080
|
||||
|
||||
print("🔍 检查端口可用性...")
|
||||
if not check_port_available(port):
|
||||
print(f"❌ 端口 {port} 被占用")
|
||||
# 尝试其他端口
|
||||
for test_port in range(8001, 8010):
|
||||
if check_port_available(test_port):
|
||||
print(f"✅ 使用端口 {test_port}")
|
||||
port = test_port
|
||||
break
|
||||
else:
|
||||
print("❌ 无法找到可用端口")
|
||||
return
|
||||
|
||||
print(f"🚀 启动简单HTTP服务器在端口 {port}")
|
||||
print("=" * 50)
|
||||
print(f"📍 本地访问: http://localhost:{port}")
|
||||
print(f"📋 测试端点:")
|
||||
print(f" 根路径: http://localhost:{port}/")
|
||||
print(f" 健康检查: http://localhost:{port}/health")
|
||||
print(f" API健康: http://localhost:{port}/api/v1/health")
|
||||
print(f" 测试端点: http://localhost:{port}/test")
|
||||
print("=" * 50)
|
||||
print("⏹️ 按 Ctrl+C 停止服务")
|
||||
|
||||
try:
|
||||
server = HTTPServer(('0.0.0.0', port), SimpleHandler)
|
||||
print(f"✅ 服务器已启动在端口 {port}")
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 服务器已停止")
|
||||
except Exception as e:
|
||||
print(f"❌ 服务器错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,79 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import time
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:3000", "http://127.0.0.1:3000"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "云盘应用 API", "version": "1.0.0"}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def health_check():
|
||||
"""基础健康检查"""
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"status": "healthy",
|
||||
"service": "cloud-drive-api",
|
||||
"environment": "development",
|
||||
"timestamp": int(time.time())
|
||||
},
|
||||
"message": "API服务运行正常"
|
||||
}
|
||||
|
||||
@app.get("/api/v1/ready")
|
||||
async def readiness_check():
|
||||
"""就绪检查 - 简化版本,不检查数据库"""
|
||||
checks = {
|
||||
"api": {
|
||||
"status": "healthy",
|
||||
"message": "API服务正常"
|
||||
},
|
||||
"database": {
|
||||
"status": "unknown",
|
||||
"message": "数据库连接未配置(简化模式)"
|
||||
},
|
||||
"redis": {
|
||||
"status": "unknown",
|
||||
"message": "Redis连接未配置(简化模式)"
|
||||
},
|
||||
"storage": {
|
||||
"status": "healthy",
|
||||
"message": "文件存储正常"
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"status": "ready",
|
||||
"checks": checks,
|
||||
"timestamp": int(time.time())
|
||||
},
|
||||
"message": "API服务已就绪(简化模式)"
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(
|
||||
"simple_main:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=True
|
||||
)
|
||||
@@ -1,208 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 简化版FastAPI服务器 - 端口8080
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
try:
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
FASTAPI_AVAILABLE = True
|
||||
except ImportError:
|
||||
print("❌ FastAPI未安装,正在安装基础依赖...")
|
||||
import subprocess
|
||||
subprocess.run([sys.executable, "-m", "pip", "install", "fastapi", "uvicorn", "requests"])
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
FASTAPI_AVAILABLE = True
|
||||
|
||||
# 尝试导入app模块
|
||||
try:
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
APP_AVAILABLE = True
|
||||
print("✅ 完整app模块可用")
|
||||
except ImportError as e:
|
||||
print(f"⚠️ App模块导入失败: {e}")
|
||||
print("🔧 使用简化模式启动...")
|
||||
APP_AVAILABLE = False
|
||||
|
||||
def create_app():
|
||||
"""创建FastAPI应用"""
|
||||
if APP_AVAILABLE:
|
||||
# 使用完整的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
try:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
except:
|
||||
# 如果settings有问题,使用默认配置
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
try:
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
except Exception as e:
|
||||
print(f"⚠️ 路由包含失败: {e}")
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.1",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health",
|
||||
"mode": "full"
|
||||
}
|
||||
|
||||
return app
|
||||
else:
|
||||
# 创建简化版本的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API (简化版)",
|
||||
description="云存储Web应用后端API - 简化版本",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API (简化版)",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/health",
|
||||
"mode": "simplified"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "服务运行正常",
|
||||
"mode": "simplified"
|
||||
}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def api_health():
|
||||
import time
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@app.get("/api/v1/test")
|
||||
async def test_endpoint():
|
||||
return {
|
||||
"message": "测试端点正常工作",
|
||||
"server": "port 8080",
|
||||
"status": "ok"
|
||||
}
|
||||
|
||||
return app
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 启动云盘应用服务器...")
|
||||
print("=" * 50)
|
||||
|
||||
# 创建必要目录
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
os.makedirs("uploads", exist_ok=True)
|
||||
|
||||
# 创建FastAPI应用
|
||||
app = create_app()
|
||||
|
||||
# 获取本机IP
|
||||
try:
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
local_ip = "127.0.0.1"
|
||||
|
||||
# 显示启动信息
|
||||
print(f"📍 本地访问:")
|
||||
print(f" 根路径: http://localhost:8080")
|
||||
print(f" API文档: http://localhost:8080/docs")
|
||||
print(f" ReDoc: http://localhost:8080/redoc")
|
||||
print(f" 健康检查: http://localhost:8080/api/v1/health")
|
||||
print(f" 测试端点: http://localhost:8080/api/v1/test")
|
||||
print("")
|
||||
print(f"🌐 网络访问:")
|
||||
print(f" 根路径: http://{local_ip}:8080")
|
||||
print(f" API文档: http://{local_ip}:8080/docs")
|
||||
print("")
|
||||
print("🔧 服务信息:")
|
||||
print(f" Python版本: {sys.version}")
|
||||
print(f" 工作目录: {os.getcwd()}")
|
||||
print(f" 启动模式: {'完整版' if APP_AVAILABLE else '简化版'}")
|
||||
print("=" * 50)
|
||||
print("⏹️ 按 Ctrl+C 停止服务")
|
||||
print("")
|
||||
|
||||
# 启动服务器
|
||||
try:
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 服务已停止")
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
print("\n💡 可能的解决方案:")
|
||||
print("1. 检查端口8080是否被占用")
|
||||
print("2. 确保安装了必要的依赖: pip install fastapi uvicorn")
|
||||
print("3. 检查防火墙设置")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 最简单的启动方式
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/health"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "服务运行正常"
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🚀 启动云盘后端服务...")
|
||||
print("📍 服务地址: http://localhost:8000")
|
||||
print("📚 API文档: http://localhost:8000/docs")
|
||||
print("❤️ 健康检查: http://localhost:8000/health")
|
||||
print("⏹️ 按 Ctrl+C 停止服务")
|
||||
print("=" * 50)
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=False
|
||||
)
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
简单的文件上传测试脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def test_auth():
|
||||
"""测试认证"""
|
||||
# 先注册
|
||||
register_data = {
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"password": "TestPass123!",
|
||||
"confirm_password": "TestPass123!"
|
||||
}
|
||||
|
||||
try:
|
||||
print("尝试注册用户...")
|
||||
reg_response = requests.post(f"{BASE_URL}/auth/register", json=register_data)
|
||||
print(f"注册状态码: {reg_response.status_code}")
|
||||
|
||||
if reg_response.status_code == 201:
|
||||
reg_result = reg_response.json()
|
||||
if reg_result.get("success"):
|
||||
token = reg_result["data"]["tokens"]["access_token"]
|
||||
print("注册成功,获得token")
|
||||
return token
|
||||
else:
|
||||
print(f"注册失败: {reg_result}")
|
||||
else:
|
||||
print(f"注册请求失败,状态码: {reg_response.status_code}")
|
||||
|
||||
# 如果注册失败,尝试登录
|
||||
print("尝试登录用户...")
|
||||
login_data = {
|
||||
"username": "testuser",
|
||||
"password": "testpass123"
|
||||
}
|
||||
|
||||
response = requests.post(f"{BASE_URL}/auth/login", json=login_data)
|
||||
print(f"登录状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"登录结果: {result.get('success', False)}")
|
||||
if result.get("success"):
|
||||
token = result["data"]["tokens"]["access_token"]
|
||||
print("登录成功,获得token")
|
||||
return token
|
||||
else:
|
||||
print(f"登录失败: {result}")
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"认证出错: {e}")
|
||||
return None
|
||||
|
||||
def test_file_upload(token):
|
||||
"""测试文件上传"""
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}"
|
||||
}
|
||||
|
||||
# 创建测试文件
|
||||
test_content = "Hello World! 测试文件内容"
|
||||
|
||||
try:
|
||||
files = {
|
||||
"file": ("test.txt", test_content.encode("utf-8"), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"description": "测试文件",
|
||||
"tags": "test,upload",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
headers=headers,
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
print(f"上传状态码: {response.status_code}")
|
||||
print(f"上传响应: {response.text}")
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
print(f"上传成功! 文件ID: {file_info['id']}")
|
||||
return file_info['id']
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"上传出错: {e}")
|
||||
return None
|
||||
|
||||
def test_file_list(token):
|
||||
"""测试获取文件列表"""
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/files/list",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
print(f"列表状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
files = result["data"]["files"]
|
||||
print(f"文件数量: {len(files)}")
|
||||
for f in files:
|
||||
print(f" - {f['original_filename']} ({f['file_size']} bytes)")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取列表出错: {e}")
|
||||
|
||||
def test_storage_info(token):
|
||||
"""测试存储信息"""
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/files/storage/info",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
print(f"存储信息状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
info = result["data"]
|
||||
print(f"存储配额: {info['total_quota']} bytes")
|
||||
print(f"已使用: {info['used_space']} bytes")
|
||||
print(f"文件数量: {info['file_count']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取存储信息出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("开始测试文件存储功能...")
|
||||
|
||||
# 1. 认证
|
||||
token = test_auth()
|
||||
if not token:
|
||||
print("认证失败,退出测试")
|
||||
exit(1)
|
||||
|
||||
# 2. 上传文件
|
||||
file_id = test_file_upload(token)
|
||||
|
||||
# 3. 获取文件列表
|
||||
test_file_list(token)
|
||||
|
||||
# 4. 获取存储信息
|
||||
test_storage_info(token)
|
||||
|
||||
print("测试完成!")
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
简单上传下载测试
|
||||
"""
|
||||
|
||||
import requests
|
||||
import sys
|
||||
import io
|
||||
|
||||
# 设置stdout编码为utf-8
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def simple_test():
|
||||
print("=== 简单上传下载测试 ===")
|
||||
|
||||
# 使用存在的用户ID (用户ID=3存在)
|
||||
user_id = 3
|
||||
print(f"使用用户ID: {user_id}")
|
||||
|
||||
# 直接测试下载现有文件 (文件ID=24, test.txt)
|
||||
file_id = 24
|
||||
print(f"\n直接下载现有文件 ID: {file_id} (test.txt)...")
|
||||
|
||||
download_data = {
|
||||
"file_id": file_id,
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
try:
|
||||
download_resp = requests.post(f"{BASE_URL}/files/download", json=download_data)
|
||||
print(f"下载状态码: {download_resp.status_code}")
|
||||
print(f"下载响应头: {dict(download_resp.headers)}")
|
||||
|
||||
if download_resp.status_code == 200:
|
||||
downloaded_content = download_resp.text
|
||||
print(f"[OK] 下载成功")
|
||||
print(f"下载内容: {downloaded_content}")
|
||||
print(f"内容长度: {len(downloaded_content)}")
|
||||
|
||||
# 读取原始文件内容进行对比
|
||||
try:
|
||||
with open(f"uploads/608408b0-02c7-449e-ba58-be43c4360333.txt", 'r', encoding='utf-8') as f:
|
||||
original_content = f.read()
|
||||
print(f"原始文件长度: {len(original_content)}")
|
||||
|
||||
if original_content == downloaded_content:
|
||||
print("[OK] 内容一致!上传下载功能正常")
|
||||
else:
|
||||
print("[ERROR] 内容不一致")
|
||||
print(f"原始: {repr(original_content[:100])}")
|
||||
print(f"下载: {repr(downloaded_content[:100])}")
|
||||
except Exception as e:
|
||||
print(f"无法读取原始文件进行对比: {e}")
|
||||
|
||||
else:
|
||||
print(f"[ERROR] 下载失败: {download_resp.text}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 下载异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# 现在测试上传新文件
|
||||
print(f"\n=== 测试上传新文件 ===")
|
||||
test_content = f"这是全新的测试文件内容\nBrand new test file content\n唯一时间戳: {hash('unique_content_' + str(__import__('time').time()))}"
|
||||
|
||||
files = {"file": (f"unique_test_{hash(test_content) % 10000}.txt", test_content, "text/plain")}
|
||||
data = {
|
||||
"user_id": str(user_id),
|
||||
"description": "新测试文件",
|
||||
"tags": "test,new",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
try:
|
||||
upload_resp = requests.post(f"{BASE_URL}/files/upload", files=files, data=data)
|
||||
print(f"上传状态码: {upload_resp.status_code}")
|
||||
|
||||
if upload_resp.status_code == 201:
|
||||
new_file_id = upload_resp.json()["data"]["file"]["id"]
|
||||
print(f"[OK] 新文件上传成功,文件ID: {new_file_id}")
|
||||
|
||||
# 立即下载新文件
|
||||
print(f"\n下载新上传的文件 ID: {new_file_id}...")
|
||||
download_data = {
|
||||
"file_id": new_file_id,
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
download_resp = requests.post(f"{BASE_URL}/files/download", json=download_data)
|
||||
if download_resp.status_code == 200:
|
||||
new_downloaded_content = download_resp.text
|
||||
print(f"[OK] 新文件下载成功")
|
||||
print(f"下载内容: {new_downloaded_content}")
|
||||
|
||||
if test_content == new_downloaded_content:
|
||||
print("[OK] 新文件内容一致!")
|
||||
else:
|
||||
print("[ERROR] 新文件内容不一致")
|
||||
else:
|
||||
print(f"[ERROR] 新文件下载失败: {download_resp.text}")
|
||||
else:
|
||||
print(f"[ERROR] 新文件上传失败: {upload_resp.text}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 新文件测试异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
simple_test()
|
||||
@@ -1,314 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
云盘应用启动脚本 - 端口8080版本
|
||||
包含完整的API测试功能
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
import subprocess
|
||||
import time
|
||||
import requests
|
||||
from typing import Dict, List
|
||||
|
||||
# 导入应用模块
|
||||
try:
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
APP_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ App模块导入失败: {e}")
|
||||
print("🔧 使用简化模式启动...")
|
||||
APP_AVAILABLE = False
|
||||
|
||||
def create_fastapi_app():
|
||||
"""创建FastAPI应用"""
|
||||
if APP_AVAILABLE:
|
||||
# 使用完整的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.1",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health"
|
||||
}
|
||||
|
||||
return app
|
||||
else:
|
||||
# 创建简化版本的应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API (简化版)",
|
||||
description="云存储Web应用后端API - 简化版本",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API (简化版)",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
"health": "/health"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"message": "服务运行正常",
|
||||
"mode": "simplified"
|
||||
}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def api_health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
return app
|
||||
|
||||
def test_api_endpoints(base_url: str = "http://localhost:8080") -> Dict[str, any]:
|
||||
"""测试API端点"""
|
||||
print("\n🧪 开始测试API端点...")
|
||||
print("=" * 50)
|
||||
|
||||
results = {}
|
||||
|
||||
# 测试端点列表
|
||||
endpoints = [
|
||||
("/", "根路径"),
|
||||
("/health", "健康检查"),
|
||||
("/api/v1/health", "API健康检查"),
|
||||
("/docs", "API文档"),
|
||||
("/redoc", "ReDoc文档"),
|
||||
("/openapi.json", "OpenAPI规范")
|
||||
]
|
||||
|
||||
for endpoint, description in endpoints:
|
||||
try:
|
||||
print(f"📍 测试 {description}: {endpoint}")
|
||||
|
||||
if endpoint in ["/docs", "/redoc"]:
|
||||
# 对于文档端点,只检查HTTP状态
|
||||
response = requests.get(f"{base_url}{endpoint}", timeout=5)
|
||||
if response.status_code == 200:
|
||||
print(f" ✅ {description} - 状态码: {response.status_code}")
|
||||
results[endpoint] = {"status": "success", "code": response.status_code}
|
||||
else:
|
||||
print(f" ❌ {description} - 状态码: {response.status_code}")
|
||||
results[endpoint] = {"status": "failed", "code": response.status_code}
|
||||
else:
|
||||
# 对于API端点,检查响应内容
|
||||
response = requests.get(f"{base_url}{endpoint}", timeout=5)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f" ✅ {description} - 响应: {data}")
|
||||
results[endpoint] = {"status": "success", "data": data}
|
||||
else:
|
||||
print(f" ❌ {description} - 状态码: {response.status_code}")
|
||||
results[endpoint] = {"status": "failed", "code": response.status_code}
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f" ❌ {description} - 连接失败")
|
||||
results[endpoint] = {"status": "connection_failed"}
|
||||
except requests.exceptions.Timeout:
|
||||
print(f" ❌ {description} - 请求超时")
|
||||
results[endpoint] = {"status": "timeout"}
|
||||
except Exception as e:
|
||||
print(f" ❌ {description} - 错误: {e}")
|
||||
results[endpoint] = {"status": "error", "error": str(e)}
|
||||
|
||||
return results
|
||||
|
||||
def start_server():
|
||||
"""启动服务器"""
|
||||
print("🚀 启动云盘应用服务...")
|
||||
print("=" * 50)
|
||||
|
||||
# 创建必要目录
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
os.makedirs("uploads", exist_ok=True)
|
||||
|
||||
# 创建FastAPI应用
|
||||
app = create_fastapi_app()
|
||||
|
||||
# 获取本机IP
|
||||
import socket
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
local_ip = s.getsockname()[0]
|
||||
s.close()
|
||||
except:
|
||||
local_ip = "127.0.0.1"
|
||||
|
||||
# 显示启动信息
|
||||
print(f"📍 本地访问:")
|
||||
print(f" 根路径: http://localhost:8080")
|
||||
print(f" API文档: http://localhost:8080/docs")
|
||||
print(f" ReDoc: http://localhost:8080/redoc")
|
||||
print(f" 健康检查: http://localhost:8080/api/v1/health")
|
||||
print("")
|
||||
print(f"🌐 网络访问:")
|
||||
print(f" 根路径: http://{local_ip}:8080")
|
||||
print(f" API文档: http://{local_ip}:8080/docs")
|
||||
print(f" ReDoc: http://{local_ip}:8080/redoc")
|
||||
print("")
|
||||
print(f"🔧 API测试:")
|
||||
print(f" 自动测试将在服务启动后执行")
|
||||
print("=" * 50)
|
||||
print("⏹️ 按 Ctrl+C 停止服务")
|
||||
print("")
|
||||
|
||||
# 启动服务器
|
||||
try:
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 服务已停止")
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def run_with_auto_test():
|
||||
"""启动服务器并自动测试"""
|
||||
print("🤖 自动模式:启动服务器并进行API测试")
|
||||
|
||||
# 启动服务器(在后台)
|
||||
import subprocess
|
||||
import signal
|
||||
import threading
|
||||
|
||||
# 启动服务器进程
|
||||
server_process = subprocess.Popen([
|
||||
sys.executable, __file__
|
||||
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
# 等待服务器启动
|
||||
print("⏳ 等待服务器启动...")
|
||||
time.sleep(5)
|
||||
|
||||
try:
|
||||
# 测试API端点
|
||||
results = test_api_endpoints()
|
||||
|
||||
# 显示测试结果
|
||||
print("\n📊 测试结果汇总:")
|
||||
print("=" * 50)
|
||||
|
||||
success_count = sum(1 for r in results.values() if r.get("status") == "success")
|
||||
total_count = len(results)
|
||||
|
||||
print(f"成功: {success_count}/{total_count}")
|
||||
|
||||
for endpoint, result in results.items():
|
||||
status = result.get("status", "unknown")
|
||||
if status == "success":
|
||||
print(f" ✅ {endpoint}")
|
||||
else:
|
||||
print(f" ❌ {endpoint} - {status}")
|
||||
|
||||
if success_count == total_count:
|
||||
print("\n🎉 所有API端点测试通过!")
|
||||
else:
|
||||
print(f"\n⚠️ {total_count - success_count} 个端点测试失败")
|
||||
|
||||
finally:
|
||||
# 停止服务器
|
||||
print("\n🛑 停止服务器...")
|
||||
server_process.terminate()
|
||||
server_process.wait()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="云盘应用启动器 - 端口8080")
|
||||
parser.add_argument("--test", action="store_true", help="启动后自动测试API端点")
|
||||
parser.add_argument("--auto", action="store_true", help="自动模式:启动并测试")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.auto:
|
||||
run_with_auto_test()
|
||||
elif args.test:
|
||||
# 先启动,再测试
|
||||
print("🚀 启动服务器...")
|
||||
app = create_fastapi_app()
|
||||
|
||||
# 在单独线程中启动服务器
|
||||
import threading
|
||||
|
||||
def run_server():
|
||||
uvicorn.run(app, host="0.0.0.0", port=8080, reload=False, log_level="warning")
|
||||
|
||||
server_thread = threading.Thread(target=run_server, daemon=True)
|
||||
server_thread.start()
|
||||
|
||||
# 等待服务器启动
|
||||
time.sleep(3)
|
||||
|
||||
# 测试API
|
||||
test_api_endpoints()
|
||||
|
||||
print("\n服务器继续运行,按 Ctrl+C 停止...")
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 服务已停止")
|
||||
else:
|
||||
# 正常启动
|
||||
start_server()
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 启动服务并测试API的脚本
|
||||
|
||||
echo "🚀 云盘应用启动和测试脚本"
|
||||
echo "=================================="
|
||||
|
||||
# 检查Python环境
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "❌ Python3 未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Python3 已安装"
|
||||
|
||||
# 检查端口8080是否被占用
|
||||
if lsof -Pi :8080 -sTCP:LISTEN -t >/dev/null ; then
|
||||
echo "❌ 端口8080已被占用"
|
||||
echo "🔧 尝试停止占用端口的进程..."
|
||||
lsof -ti:8080 | xargs kill -9 2>/dev/null || echo "无法停止进程"
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# 启动服务器(后台)
|
||||
echo "🚀 启动服务器在端口8080..."
|
||||
python3 start_8080.py &
|
||||
SERVER_PID=$!
|
||||
|
||||
# 等待服务器启动
|
||||
echo "⏳ 等待服务器启动..."
|
||||
sleep 5
|
||||
|
||||
# 测试服务器是否启动成功
|
||||
if curl -s http://localhost:8080/health > /dev/null; then
|
||||
echo "✅ 服务器启动成功"
|
||||
else
|
||||
echo "❌ 服务器启动失败"
|
||||
kill $SERVER_PID 2>/dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 运行API测试
|
||||
echo ""
|
||||
echo "🧪 开始API测试..."
|
||||
echo "=================================="
|
||||
python3 test_api_8080.py
|
||||
|
||||
# 停止服务器
|
||||
echo ""
|
||||
echo "🛑 停止服务器..."
|
||||
kill $SERVER_PID 2>/dev/null
|
||||
wait $SERVER_PID 2>/dev/null
|
||||
|
||||
echo ""
|
||||
echo "✅ 测试完成!"
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 简化版启动脚本,解决模块导入问题
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = Path(__file__).parent
|
||||
sys.path.insert(0, str(current_dir))
|
||||
|
||||
print(f"当前目录: {current_dir}")
|
||||
print(f"Python路径: {sys.path[:3]}...")
|
||||
|
||||
# 检查必要目录
|
||||
required_dirs = ['app', 'app/core', 'app/api/v1/endpoints']
|
||||
for dir_path in required_dirs:
|
||||
full_path = current_dir / dir_path
|
||||
if not full_path.exists():
|
||||
print(f"创建目录: {dir_path}")
|
||||
full_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 创建__init__.py
|
||||
init_file = full_path / '__init__.py'
|
||||
if not init_file.exists():
|
||||
init_file.write_text(f'"""{dir_path} 模块包"""\n')
|
||||
|
||||
try:
|
||||
# 测试导入
|
||||
print("测试导入模块...")
|
||||
from app.core.config import settings
|
||||
print("✓ app.core.config 导入成功")
|
||||
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
print("✓ app.api.v1.endpoints 导入成功")
|
||||
|
||||
# 启动FastAPI应用
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # 临时允许所有来源
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "云盘应用 API", "version": "1.0.1"}
|
||||
|
||||
print("✓ FastAPI应用创建成功")
|
||||
|
||||
# 启动服务
|
||||
print("启动服务...")
|
||||
print("访问地址: http://localhost:8000")
|
||||
print("API文档: http://localhost:8000/docs")
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=False # 关闭热重载避免问题
|
||||
)
|
||||
|
||||
except ImportError as e:
|
||||
print(f"导入错误: {e}")
|
||||
print("\n可能的解决方案:")
|
||||
print("1. 确保在正确的目录运行(包含main.py的目录)")
|
||||
print("2. 安装依赖: pip install fastapi uvicorn sqlalchemy pymysql redis python-jose passlib python-multipart pydantic pydantic-settings httpx python-dotenv loguru")
|
||||
print("3. 检查app目录结构是否完整")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"启动错误: {e}")
|
||||
sys.exit(1)
|
||||
@@ -1,76 +0,0 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app.core.config import settings
|
||||
from app.api.v1.endpoints import health, auth, files
|
||||
import uvicorn
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 简单的日志打印函数
|
||||
def log_info(message):
|
||||
"""打印INFO级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] INFO: {message}")
|
||||
|
||||
def log_error(message):
|
||||
"""打印ERROR级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] ERROR: {message}")
|
||||
|
||||
def log_debug(message):
|
||||
"""打印DEBUG级别日志"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"[{timestamp}] DEBUG: {message}")
|
||||
|
||||
# 确保logs目录存在
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
|
||||
log_info("=== Test Server Starting ===")
|
||||
log_info(f"Python version: {sys.version}")
|
||||
log_info(f"Working directory: {os.getcwd()}")
|
||||
log_info("Simple print logger configured")
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用 API (测试环境)",
|
||||
description="现代化的云存储Web应用后端API - 测试环境",
|
||||
version="1.0.0-test",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.ALLOWED_HOSTS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 包含路由
|
||||
app.include_router(health.router, prefix="/api/v1", tags=["health"])
|
||||
app.include_router(auth.router, prefix="/api/v1/auth", tags=["authentication"])
|
||||
app.include_router(files.router, prefix="/api/v1/files", tags=["files"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "云盘应用 API (测试环境)", "version": "1.0.1-test", "port": 8010}
|
||||
|
||||
@app.get("/test")
|
||||
async def test():
|
||||
return {"status": "ok", "message": "测试服务器运行正常", "port": 8010}
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 设置测试端口
|
||||
test_port = 8010
|
||||
|
||||
# 临时设置环境变量覆盖配置
|
||||
os.environ["PORT"] = str(test_port)
|
||||
|
||||
uvicorn.run(
|
||||
"test-main:app",
|
||||
host="0.0.0.0",
|
||||
port=test_port,
|
||||
reload=False # 测试环境不使用热重载
|
||||
)
|
||||
@@ -1,231 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
API测试脚本 - 测试端口8080上的所有端点
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, List
|
||||
|
||||
class CloudDriveAPITester:
|
||||
def __init__(self, base_url: str = "http://localhost:8080"):
|
||||
self.base_url = base_url
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
})
|
||||
|
||||
def test_endpoint(self, method: str, endpoint: str, data: Dict = None, description: str = "") -> Dict:
|
||||
"""测试单个端点"""
|
||||
url = f"{self.base_url}{endpoint}"
|
||||
|
||||
try:
|
||||
print(f"📍 {description or f'{method.upper()} {endpoint}'}")
|
||||
|
||||
if method.upper() == 'GET':
|
||||
response = self.session.get(url, timeout=10)
|
||||
elif method.upper() == 'POST':
|
||||
response = self.session.post(url, json=data, timeout=10)
|
||||
elif method.upper() == 'PUT':
|
||||
response = self.session.put(url, json=data, timeout=10)
|
||||
elif method.upper() == 'DELETE':
|
||||
response = self.session.delete(url, timeout=10)
|
||||
else:
|
||||
return {"status": "error", "message": f"不支持的HTTP方法: {method}"}
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
data = response.json()
|
||||
print(f" ✅ 成功 (200): {json.dumps(data, ensure_ascii=False, indent=2)[:200]}...")
|
||||
return {
|
||||
"status": "success",
|
||||
"code": response.status_code,
|
||||
"data": data
|
||||
}
|
||||
except json.JSONDecodeError:
|
||||
print(f" ✅ 成功 (200): {response.text[:200]}...")
|
||||
return {
|
||||
"status": "success",
|
||||
"code": response.status_code,
|
||||
"text": response.text
|
||||
}
|
||||
else:
|
||||
print(f" ❌ 失败 ({response.status_code}): {response.text[:200]}...")
|
||||
return {
|
||||
"status": "failed",
|
||||
"code": response.status_code,
|
||||
"error": response.text
|
||||
}
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f" ❌ 连接失败 - 无法连接到 {url}")
|
||||
return {"status": "connection_failed", "error": "Connection failed"}
|
||||
except requests.exceptions.Timeout:
|
||||
print(f" ❌ 请求超时")
|
||||
return {"status": "timeout", "error": "Request timeout"}
|
||||
except Exception as e:
|
||||
print(f" ❌ 未知错误: {e}")
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
def test_basic_endpoints(self) -> Dict:
|
||||
"""测试基础端点"""
|
||||
print("🔍 测试基础端点")
|
||||
print("=" * 50)
|
||||
|
||||
results = {}
|
||||
|
||||
# 测试根路径
|
||||
results["root"] = self.test_endpoint("GET", "/", description="根路径")
|
||||
|
||||
# 测试健康检查端点
|
||||
results["health"] = self.test_endpoint("GET", "/health", description="健康检查")
|
||||
results["api_health"] = self.test_endpoint("GET", "/api/v1/health", description="API健康检查")
|
||||
|
||||
# 测试文档端点
|
||||
results["docs"] = self.test_endpoint("GET", "/docs", description="API文档")
|
||||
results["redoc"] = self.test_endpoint("GET", "/redoc", description="ReDoc文档")
|
||||
results["openapi"] = self.test_endpoint("GET", "/openapi.json", description="OpenAPI规范")
|
||||
|
||||
return results
|
||||
|
||||
def test_auth_endpoints(self) -> Dict:
|
||||
"""测试认证端点"""
|
||||
print("\n🔐 测试认证端点")
|
||||
print("=" * 50)
|
||||
|
||||
results = {}
|
||||
|
||||
# 测试注册端点
|
||||
test_user = {
|
||||
"username": "testuser8080",
|
||||
"email": "test8080@example.com",
|
||||
"password": "Test123!@#",
|
||||
"confirm_password": "Test123!@#"
|
||||
}
|
||||
results["register"] = self.test_endpoint("POST", "/api/v1/auth/register",
|
||||
data=test_user, description="用户注册")
|
||||
|
||||
# 测试登录端点
|
||||
login_data = {
|
||||
"username": "testuser8080",
|
||||
"password": "Test123!@#"
|
||||
}
|
||||
results["login"] = self.test_endpoint("POST", "/api/v1/auth/token",
|
||||
data=login_data, description="用户登录")
|
||||
|
||||
return results
|
||||
|
||||
def test_file_endpoints(self) -> Dict:
|
||||
"""测试文件端点"""
|
||||
print("\n📁 测试文件端点")
|
||||
print("=" * 50)
|
||||
|
||||
results = {}
|
||||
|
||||
# 测试文件列表
|
||||
results["list_files"] = self.test_endpoint("GET", "/api/v1/files",
|
||||
description="获取文件列表")
|
||||
|
||||
# 测试文件上传(模拟)
|
||||
results["upload_info"] = self.test_endpoint("GET", "/api/v1/files",
|
||||
description="文件上传信息")
|
||||
|
||||
return results
|
||||
|
||||
def run_all_tests(self) -> Dict:
|
||||
"""运行所有测试"""
|
||||
print("🧪 开始API测试 - 端口8080")
|
||||
print("=" * 60)
|
||||
print(f"测试目标: {self.base_url}")
|
||||
print("=" * 60)
|
||||
|
||||
all_results = {}
|
||||
|
||||
# 测试基础端点
|
||||
basic_results = self.test_basic_endpoints()
|
||||
all_results.update(basic_results)
|
||||
|
||||
# 测试认证端点
|
||||
auth_results = self.test_auth_endpoints()
|
||||
all_results.update(auth_results)
|
||||
|
||||
# 测试文件端点
|
||||
file_results = self.test_file_endpoints()
|
||||
all_results.update(file_results)
|
||||
|
||||
# 生成测试报告
|
||||
self.generate_report(all_results)
|
||||
|
||||
return all_results
|
||||
|
||||
def generate_report(self, results: Dict):
|
||||
"""生成测试报告"""
|
||||
print("\n📊 测试报告")
|
||||
print("=" * 60)
|
||||
|
||||
total_tests = len(results)
|
||||
success_tests = sum(1 for r in results.values() if r.get("status") == "success")
|
||||
failed_tests = total_tests - success_tests
|
||||
|
||||
print(f"总测试数: {total_tests}")
|
||||
print(f"成功: {success_tests}")
|
||||
print(f"失败: {failed_tests}")
|
||||
print(f"成功率: {(success_tests/total_tests*100):.1f}%")
|
||||
|
||||
if failed_tests == 0:
|
||||
print("\n🎉 所有测试通过!API服务运行正常")
|
||||
else:
|
||||
print(f"\n⚠️ 有 {failed_tests} 个测试失败,请检查服务状态")
|
||||
|
||||
print("\n📋 详细结果:")
|
||||
for endpoint, result in results.items():
|
||||
status = result.get("status", "unknown")
|
||||
if status == "success":
|
||||
print(f" ✅ {endpoint}")
|
||||
elif status == "connection_failed":
|
||||
print(f" 🔌 {endpoint} - 连接失败")
|
||||
elif status == "timeout":
|
||||
print(f" ⏰ {endpoint} - 超时")
|
||||
else:
|
||||
print(f" ❌ {endpoint} - {status}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="云盘API测试工具")
|
||||
parser.add_argument("--url", default="http://localhost:8080",
|
||||
help="API基础URL (默认: http://localhost:8080)")
|
||||
parser.add_argument("--wait", type=int, default=0,
|
||||
help="启动前等待时间(秒)")
|
||||
parser.add_argument("--basic", action="store_true", help="只测试基础端点")
|
||||
parser.add_argument("--auth", action="store_true", help="只测试认证端点")
|
||||
parser.add_argument("--files", action="store_true", help="只测试文件端点")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.wait > 0:
|
||||
print(f"⏳ 等待 {args.wait} 秒后开始测试...")
|
||||
time.sleep(args.wait)
|
||||
|
||||
tester = CloudDriveAPITester(args.url)
|
||||
|
||||
try:
|
||||
if args.basic:
|
||||
tester.test_basic_endpoints()
|
||||
elif args.auth:
|
||||
tester.test_auth_endpoints()
|
||||
elif args.files:
|
||||
tester.test_file_endpoints()
|
||||
else:
|
||||
tester.run_all_tests()
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 测试已中断")
|
||||
except Exception as e:
|
||||
print(f"\n❌ 测试过程中出现错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,50 +0,0 @@
|
||||
import sys
|
||||
import traceback
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from app.core.database import get_db
|
||||
from app.services.user_service import UserService
|
||||
from app.schemas.auth import UserRegister
|
||||
|
||||
def test_api_exception():
|
||||
"""测试API异常处理"""
|
||||
try:
|
||||
print("=== 测试API异常处理 ===")
|
||||
|
||||
# 导入应用
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
from main import app
|
||||
|
||||
# 创建测试客户端
|
||||
client = TestClient(app)
|
||||
|
||||
# 测试重复邮箱注册
|
||||
print("1. 测试重复邮箱注册...")
|
||||
response = client.post("/api/v1/auth/register", json={
|
||||
"username": "test_api",
|
||||
"email": "user@example.com", # 重复邮箱
|
||||
"password": "TestPass123!",
|
||||
"confirm_password": "TestPass123!"
|
||||
})
|
||||
|
||||
print(f" HTTP状态码: {response.status_code}")
|
||||
print(f" 响应内容: {response.json()}")
|
||||
|
||||
if response.status_code == 400:
|
||||
print(" ✓ 正确返回400错误")
|
||||
detail = response.json().get("detail", {})
|
||||
if detail.get("code") == "EMAIL_EXISTS":
|
||||
print(" ✓ 正确返回EMAIL_EXISTS错误码")
|
||||
else:
|
||||
print(f" ✗ 错误码不正确: {detail.get('code')}")
|
||||
else:
|
||||
print(f" ✗ 状态码不正确,期望400,实际{response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"测试过程出错: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_api_exception()
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
|
||||
def test_api():
|
||||
base_url = "http://localhost:8080"
|
||||
|
||||
print("Testing API endpoints...")
|
||||
print("=" * 40)
|
||||
|
||||
# Test endpoints
|
||||
endpoints = [
|
||||
("/", "Root endpoint"),
|
||||
("/health", "Health check"),
|
||||
("/api/v1/health", "API health"),
|
||||
("/test", "Test endpoint"),
|
||||
("/info", "Info endpoint")
|
||||
]
|
||||
|
||||
results = {}
|
||||
|
||||
for endpoint, description in endpoints:
|
||||
try:
|
||||
print(f"Testing {description}: {endpoint}")
|
||||
response = requests.get(f"{base_url}{endpoint}", timeout=5)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print(f" SUCCESS: {data}")
|
||||
results[endpoint] = {"status": "success", "data": data}
|
||||
else:
|
||||
print(f" FAILED: Status code {response.status_code}")
|
||||
results[endpoint] = {"status": "failed", "code": response.status_code}
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f" FAILED: Connection error")
|
||||
results[endpoint] = {"status": "connection_error"}
|
||||
except requests.exceptions.Timeout:
|
||||
print(f" FAILED: Timeout")
|
||||
results[endpoint] = {"status": "timeout"}
|
||||
except Exception as e:
|
||||
print(f" FAILED: {e}")
|
||||
results[endpoint] = {"status": "error", "error": str(e)}
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 40)
|
||||
print("Test Summary:")
|
||||
|
||||
success_count = sum(1 for r in results.values() if r.get("status") == "success")
|
||||
total_count = len(results)
|
||||
|
||||
print(f"Total tests: {total_count}")
|
||||
print(f"Successful: {success_count}")
|
||||
print(f"Failed: {total_count - success_count}")
|
||||
print(f"Success rate: {(success_count/total_count*100):.1f}%")
|
||||
|
||||
if success_count == total_count:
|
||||
print("\n🎉 All API tests passed!")
|
||||
print("✅ Server is running correctly on port 8080")
|
||||
print("✅ You can access:")
|
||||
print(" - http://localhost:8080/docs (Swagger UI)")
|
||||
print(" - http://localhost:8080/redoc (ReDoc)")
|
||||
else:
|
||||
print(f"\n⚠️ {total_count - success_count} tests failed")
|
||||
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Wait a bit for server to fully start
|
||||
time.sleep(2)
|
||||
test_api()
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 最基础的FastAPI测试服务器
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
print("🔍 检查Python环境...")
|
||||
print(f"Python版本: {sys.version}")
|
||||
print(f"当前目录: {os.getcwd()}")
|
||||
|
||||
# 测试导入
|
||||
try:
|
||||
import fastapi
|
||||
print(f"✅ FastAPI可用: {fastapi.__version__}")
|
||||
except ImportError as e:
|
||||
print(f"❌ FastAPI不可用: {e}")
|
||||
print("正在安装FastAPI...")
|
||||
import subprocess
|
||||
subprocess.run([sys.executable, "-m", "pip", "install", "fastapi", "uvicorn"])
|
||||
import fastapi
|
||||
print(f"✅ FastAPI安装成功: {fastapi.__version__}")
|
||||
|
||||
try:
|
||||
import uvicorn
|
||||
print(f"✅ Uvicorn可用: {uvicorn.__version__}")
|
||||
except ImportError as e:
|
||||
print(f"❌ Uvicorn不可用: {e}")
|
||||
print("正在安装Uvicorn...")
|
||||
import subprocess
|
||||
subprocess.run([sys.executable, "-m", "pip", "install", "uvicorn"])
|
||||
import uvicorn
|
||||
print(f"✅ Uvicorn安装成功: {uvicorn.__version__}")
|
||||
|
||||
# 创建最简单的FastAPI应用
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI(
|
||||
title="云盘应用测试",
|
||||
description="测试服务器",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "服务器运行正常", "port": 8080, "status": "ok"}
|
||||
|
||||
@app.get("/test")
|
||||
async def test():
|
||||
return {"test": "ok", "server": "working"}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def health():
|
||||
import time
|
||||
return {"status": "healthy", "timestamp": time.time(), "server": "port 8080"}
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🚀 启动测试服务器...")
|
||||
print("=" * 40)
|
||||
print("📍 本地访问: http://localhost:8080")
|
||||
print("📚 API文档: http://localhost:8080/docs")
|
||||
print("=" * 40)
|
||||
print("按 Ctrl+C 停止服务")
|
||||
|
||||
try:
|
||||
uvicorn.run(app, host="0.0.0.0", port=8080, log_level="info")
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
@@ -1,52 +0,0 @@
|
||||
import sys
|
||||
from app.core.token_blacklist import token_blacklist
|
||||
from app.core.security import verify_token
|
||||
|
||||
def test_blacklist():
|
||||
"""测试令牌黑名单功能"""
|
||||
try:
|
||||
print("=== 测试令牌黑名单功能 ===")
|
||||
|
||||
# 使用之前获取的令牌
|
||||
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3IiwidXNlcm5hbWUiOiJ0ZXN0bWUiLCJlbWFpbCI6InRlc3RtZUBleGFtcGxlLmNvbSIsImV4cCI6MTc2MDE3MjI5OCwidHlwZSI6ImFjY2VzcyJ9.Fp9cZ4rDelm6mpxc0cYqXJJ4ne-xG90QuAx1UNfJBIY"
|
||||
|
||||
# 1. 检查令牌是否在黑名单中
|
||||
print("1. 检查令牌是否在黑名单中...")
|
||||
is_blacklisted = token_blacklist.is_blacklisted(token)
|
||||
print(f" 令牌在黑名单中: {is_blacklisted}")
|
||||
|
||||
# 2. 验证令牌
|
||||
print("2. 验证令牌...")
|
||||
payload = verify_token(token, "access")
|
||||
if payload:
|
||||
print(f" 令牌验证成功,用户ID: {payload.get('sub')}")
|
||||
else:
|
||||
print(" 令牌验证失败")
|
||||
|
||||
# 3. 手动将令牌加入黑名单
|
||||
print("3. 将令牌加入黑名单...")
|
||||
token_blacklist.add_token(token)
|
||||
print(" 令牌已加入黑名单")
|
||||
|
||||
# 4. 再次检查
|
||||
print("4. 再次检查令牌是否在黑名单中...")
|
||||
is_blacklisted = token_blacklist.is_blacklisted(token)
|
||||
print(f" 令牌在黑名单中: {is_blacklisted}")
|
||||
|
||||
# 5. 再次验证令牌
|
||||
print("5. 再次验证令牌...")
|
||||
payload = verify_token(token, "access")
|
||||
if payload:
|
||||
print(f" 令牌验证成功,用户ID: {payload.get('sub')}")
|
||||
else:
|
||||
print(" 令牌验证失败(预期结果)")
|
||||
|
||||
print("\n=== 黑名单功能测试完成 ===")
|
||||
|
||||
except Exception as e:
|
||||
print(f"测试过程出错: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_blacklist()
|
||||
@@ -1,57 +0,0 @@
|
||||
import pymysql
|
||||
from app.core.config import settings
|
||||
|
||||
def test_mysql_connection():
|
||||
try:
|
||||
print(f"正在连接数据库: {settings.DATABASE_URL}")
|
||||
|
||||
# 解析连接字符串
|
||||
import re
|
||||
pattern = r'mysql\+pymysql://([^:]+):([^@]+)@([^:]+):(\d+)/(.+)'
|
||||
match = re.match(pattern, settings.DATABASE_URL)
|
||||
|
||||
if match:
|
||||
username, password, host, port, database = match.groups()
|
||||
print(f"主机: {host}")
|
||||
print(f"端口: {port}")
|
||||
print(f"用户: {username}")
|
||||
print(f"数据库: {database}")
|
||||
|
||||
# 尝试连接
|
||||
connection = pymysql.connect(
|
||||
host=host,
|
||||
port=int(port),
|
||||
user=username,
|
||||
password=password,
|
||||
database=database,
|
||||
charset='utf8mb4'
|
||||
)
|
||||
|
||||
print("MySQL数据库连接成功!")
|
||||
|
||||
# 测试查询
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT VERSION()")
|
||||
version = cursor.fetchone()
|
||||
print(f"MySQL版本: {version[0]}")
|
||||
|
||||
cursor.execute("SHOW TABLES")
|
||||
tables = cursor.fetchall()
|
||||
print(f"现有表数量: {len(tables)}")
|
||||
for table in tables:
|
||||
print(f"- {table[0]}")
|
||||
|
||||
connection.close()
|
||||
return True
|
||||
|
||||
else:
|
||||
print("连接字符串格式错误")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"数据库连接失败: {str(e)}")
|
||||
print(f"错误类型: {type(e).__name__}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_mysql_connection()
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
直接测试文件写入,绕过所有业务逻辑
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
def test_direct_write():
|
||||
"""直接测试文件写入"""
|
||||
|
||||
print("=== 直接文件写入测试 ===")
|
||||
|
||||
# 测试内容
|
||||
test_content = b"Direct write test content - bypassing all logic"
|
||||
|
||||
# 文件路径
|
||||
file_path = "uploads/direct_test_write.txt"
|
||||
|
||||
print(f"测试内容大小: {len(test_content)} bytes")
|
||||
print(f"目标路径: {os.path.abspath(file_path)}")
|
||||
|
||||
# 确保目录存在
|
||||
os.makedirs("uploads", exist_ok=True)
|
||||
|
||||
# 方法1: 使用 open 直接写入
|
||||
print("\n--- 方法1: 直接 open 写入 ---")
|
||||
try:
|
||||
with open(file_path, "wb") as f:
|
||||
written = f.write(test_content)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
print(f"写入字节数: {written}")
|
||||
|
||||
if os.path.exists(file_path):
|
||||
size = os.path.getsize(file_path)
|
||||
print(f"文件大小: {size} bytes")
|
||||
|
||||
if size == len(test_content):
|
||||
print("✅ 方法1成功!")
|
||||
else:
|
||||
print(f"❌ 方法1失败: 大小不匹配 {size} != {len(test_content)}")
|
||||
else:
|
||||
print("❌ 方法1失败: 文件不存在")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 方法1异常: {e}")
|
||||
|
||||
# 方法2: 使用 Path 写入
|
||||
print("\n--- 方法2: 使用 pathlib.Path 写入 ---")
|
||||
from pathlib import Path
|
||||
file_path2 = "uploads/path_test_write.txt"
|
||||
|
||||
try:
|
||||
Path(file_path2).write_bytes(test_content)
|
||||
|
||||
if os.path.exists(file_path2):
|
||||
size = os.path.getsize(file_path2)
|
||||
print(f"文件大小: {size} bytes")
|
||||
|
||||
if size == len(test_content):
|
||||
print("✅ 方法2成功!")
|
||||
else:
|
||||
print(f"❌ 方法2失败: 大小不匹配 {size} != {len(test_content)}")
|
||||
else:
|
||||
print("❌ 方法2失败: 文件不存在")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 方法2异常: {e}")
|
||||
|
||||
# 方法3: 检查现有文件
|
||||
print("\n--- 方法3: 检查现有损坏的文件 ---")
|
||||
existing_files = [f for f in os.listdir("uploads") if f.endswith('.txt')]
|
||||
print(f"现有文本文件: {existing_files}")
|
||||
|
||||
for filename in existing_files[:3]: # 只检查前3个
|
||||
file_path = os.path.join("uploads", filename)
|
||||
try:
|
||||
size = os.path.getsize(file_path)
|
||||
print(f"{filename}: {size} bytes")
|
||||
|
||||
if size > 0:
|
||||
with open(file_path, 'rb') as f:
|
||||
content = f.read(min(100, size))
|
||||
print(f" 内容预览: {content}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" 读取错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_direct_write()
|
||||
@@ -1,214 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试文件上传功能的脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def test_file_upload():
|
||||
"""测试文件上传功能"""
|
||||
|
||||
# 1. 先注册或登录用户获取token
|
||||
print("1. 测试用户登录...")
|
||||
login_data = {
|
||||
"username": "testuser",
|
||||
"password": "testpass123"
|
||||
}
|
||||
|
||||
try:
|
||||
# 尝试登录
|
||||
response = requests.post(f"{BASE_URL}/auth/login", json=login_data)
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
token = result["data"]["tokens"]["access_token"]
|
||||
print("[OK] 登录成功")
|
||||
else:
|
||||
print("[ERROR] 登录失败,尝试注册...")
|
||||
# 如果登录失败,先注册
|
||||
register_data = {
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"password": "testpass123"
|
||||
}
|
||||
register_response = requests.post(f"{BASE_URL}/auth/register", json=register_data)
|
||||
if register_response.status_code == 201:
|
||||
register_result = register_response.json()
|
||||
if register_result.get("success"):
|
||||
token = register_result["data"]["tokens"]["access_token"]
|
||||
print("✓ 注册成功")
|
||||
else:
|
||||
print(f"✗ 注册失败: {register_result}")
|
||||
return
|
||||
else:
|
||||
print(f"✗ 注册请求失败: {register_response.status_code}")
|
||||
return
|
||||
|
||||
# 注册后再次登录
|
||||
login_response = requests.post(f"{BASE_URL}/auth/login", json=login_data)
|
||||
if login_response.status_code == 200:
|
||||
login_result = login_response.json()
|
||||
if login_result.get("success"):
|
||||
token = login_result["data"]["tokens"]["access_token"]
|
||||
print("✓ 登录成功")
|
||||
else:
|
||||
print(f"登录请求失败: {response.status_code}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"认证过程出错: {e}")
|
||||
return
|
||||
|
||||
# 设置认证头
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}"
|
||||
}
|
||||
|
||||
# 2. 创建测试文件
|
||||
print("\n2. 创建测试文件...")
|
||||
test_file_path = "test_file.txt"
|
||||
test_content = "这是一个测试文件内容\nHello, World!\n测试文件上传功能"
|
||||
|
||||
try:
|
||||
with open(test_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(test_content)
|
||||
print(f"✓ 创建测试文件: {test_file_path}")
|
||||
except Exception as e:
|
||||
print(f"✗ 创建测试文件失败: {e}")
|
||||
return
|
||||
|
||||
# 3. 测试文件上传
|
||||
print("\n3. 测试文件上传...")
|
||||
try:
|
||||
with open(test_file_path, "rb") as f:
|
||||
files = {
|
||||
"file": (test_file_path, f, "text/plain")
|
||||
}
|
||||
data = {
|
||||
"description": "测试文件描述",
|
||||
"tags": "测试,文件,上传",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
upload_response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
headers=headers,
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
if upload_response.status_code == 201:
|
||||
upload_result = upload_response.json()
|
||||
if upload_result.get("success"):
|
||||
file_info = upload_result["data"]["file"]
|
||||
print(f"✓ 文件上传成功!")
|
||||
print(f" 文件ID: {file_info['id']}")
|
||||
print(f" 文件名: {file_info['original_filename']}")
|
||||
print(f" 文件大小: {file_info['file_size']} 字节")
|
||||
|
||||
# 保存文件ID用于后续测试
|
||||
file_id = file_info["id"]
|
||||
else:
|
||||
print(f"✗ 文件上传失败: {upload_result}")
|
||||
return
|
||||
else:
|
||||
print(f"✗ 文件上传请求失败: {upload_response.status_code}")
|
||||
print(f"响应内容: {upload_response.text}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 文件上传过程出错: {e}")
|
||||
return
|
||||
|
||||
# 4. 测试获取文件列表
|
||||
print("\n4. 测试获取文件列表...")
|
||||
try:
|
||||
list_response = requests.get(
|
||||
f"{BASE_URL}/files/list",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if list_response.status_code == 200:
|
||||
list_result = list_response.json()
|
||||
if list_result.get("success"):
|
||||
files_list = list_result["data"]["files"]
|
||||
pagination = list_result["data"]["pagination"]
|
||||
print(f"✓ 获取文件列表成功!")
|
||||
print(f" 文件数量: {len(files_list)}")
|
||||
print(f" 总数: {pagination['total']}")
|
||||
for file_item in files_list:
|
||||
print(f" - {file_item['original_filename']} ({file_item['file_size']} 字节)")
|
||||
else:
|
||||
print(f"✗ 获取文件列表失败: {list_result}")
|
||||
else:
|
||||
print(f"✗ 获取文件列表请求失败: {list_response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 获取文件列表过程出错: {e}")
|
||||
|
||||
# 5. 测试获取存储信息
|
||||
print("\n5. 测试获取存储信息...")
|
||||
try:
|
||||
storage_response = requests.get(
|
||||
f"{BASE_URL}/files/storage/info",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if storage_response.status_code == 200:
|
||||
storage_result = storage_response.json()
|
||||
if storage_result.get("success"):
|
||||
storage_info = storage_result["data"]
|
||||
print(f"✓ 获取存储信息成功!")
|
||||
print(f" 总配额: {storage_info['total_quota']} 字节")
|
||||
print(f" 已使用: {storage_info['used_space']} 字节")
|
||||
print(f" 可用空间: {storage_info['available_space']} 字节")
|
||||
print(f" 使用百分比: {storage_info['usage_percentage']:.2f}%")
|
||||
print(f" 文件数量: {storage_info['file_count']}")
|
||||
else:
|
||||
print(f"✗ 获取存储信息失败: {storage_result}")
|
||||
else:
|
||||
print(f"✗ 获取存储信息请求失败: {storage_response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 获取存储信息过程出错: {e}")
|
||||
|
||||
# 6. 测试文件下载
|
||||
print("\n6. 测试文件下载...")
|
||||
try:
|
||||
download_response = requests.get(
|
||||
f"{BASE_URL}/files/{file_id}/download",
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if download_response.status_code == 200:
|
||||
print(f"✓ 文件下载成功!")
|
||||
print(f" 下载内容长度: {len(download_response.content)} 字节")
|
||||
# 保存下载的文件
|
||||
downloaded_file_path = "downloaded_test_file.txt"
|
||||
with open(downloaded_file_path, "wb") as f:
|
||||
f.write(download_response.content)
|
||||
print(f" 已保存为: {downloaded_file_path}")
|
||||
else:
|
||||
print(f"✗ 文件下载请求失败: {download_response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 文件下载过程出错: {e}")
|
||||
|
||||
# 7. 清理测试文件
|
||||
print("\n7. 清理测试文件...")
|
||||
try:
|
||||
Path(test_file_path).unlink(missing_ok=True)
|
||||
Path("downloaded_test_file.txt").unlink(missing_ok=True)
|
||||
print("✓ 清理完成")
|
||||
except Exception as e:
|
||||
print(f"✗ 清理过程出错: {e}")
|
||||
|
||||
print("\n🎉 文件存储功能测试完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_file_upload()
|
||||
@@ -1,194 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
简化版文件API测试脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
# 固定用户ID
|
||||
USER_ID = 8
|
||||
|
||||
def test_file_upload():
|
||||
"""测试文件上传"""
|
||||
test_content = "Hello World! 这是测试文件内容。"
|
||||
|
||||
try:
|
||||
files = {
|
||||
"file": ("test.txt", test_content.encode("utf-8"), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"description": "测试文件上传",
|
||||
"tags": "test,upload",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
print(f"上传状态码: {response.status_code}")
|
||||
print(f"上传响应: {response.text}")
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
print(f"上传成功! 文件ID: {file_info['id']}")
|
||||
return file_info['id']
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"上传出错: {e}")
|
||||
return None
|
||||
|
||||
def test_file_list():
|
||||
"""测试获取文件列表"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"page": 1,
|
||||
"size": 20
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/list",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"列表状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
files = result["data"]["files"]
|
||||
print(f"文件数量: {len(files)}")
|
||||
for f in files:
|
||||
print(f" - {f['original_filename']} (ID: {f['id']}, 大小: {f['file_size']} bytes)")
|
||||
return files
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取列表出错: {e}")
|
||||
return []
|
||||
|
||||
def test_storage_info():
|
||||
"""测试存储信息"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": USER_ID
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/storage/info",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"存储信息状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
info = result["data"]
|
||||
print(f"存储信息:")
|
||||
print(f" 总配额: {info['total_quota']} bytes")
|
||||
print(f" 已使用: {info['used_space']} bytes")
|
||||
print(f" 可用空间: {info['available_space']} bytes")
|
||||
print(f" 使用百分比: {info['usage_percentage']:.2f}%")
|
||||
print(f" 文件数量: {info['file_count']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取存储信息出错: {e}")
|
||||
|
||||
def test_file_info(file_id):
|
||||
"""测试获取文件信息"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/info",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"文件信息状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
info = result["data"]
|
||||
print(f"文件信息:")
|
||||
print(f" 文件名: {info['original_filename']}")
|
||||
print(f" 大小: {info['file_size']} bytes")
|
||||
print(f" MIME类型: {info['mime_type']}")
|
||||
print(f" 是否图片: {info['is_image']}")
|
||||
print(f" 是否文档: {info['is_document']}")
|
||||
print(f" 格式化大小: {info['size_formatted']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取文件信息出错: {e}")
|
||||
|
||||
def test_file_delete(file_id):
|
||||
"""测试删除文件"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": USER_ID,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/delete",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"删除状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
print("文件删除成功")
|
||||
else:
|
||||
print(f"删除失败: {result}")
|
||||
else:
|
||||
print(f"删除请求失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"删除文件出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(f"开始测试文件API (用户ID: {USER_ID})...")
|
||||
|
||||
# 1. 测试存储信息
|
||||
print("\n=== 测试存储信息 ===")
|
||||
test_storage_info()
|
||||
|
||||
# 2. 上传文件
|
||||
print("\n=== 测试文件上传 ===")
|
||||
file_id = test_file_upload()
|
||||
|
||||
# 3. 获取文件列表
|
||||
print("\n=== 测试文件列表 ===")
|
||||
files = test_file_list()
|
||||
|
||||
# 4. 获取文件信息
|
||||
if file_id:
|
||||
print("\n=== 测试文件信息 ===")
|
||||
test_file_info(file_id)
|
||||
|
||||
# 5. 删除文件
|
||||
print("\n=== 测试文件删除 ===")
|
||||
test_file_delete(file_id)
|
||||
|
||||
# 6. 再次检查文件列表和存储信息
|
||||
print("\n=== 最终检查 ===")
|
||||
test_file_list()
|
||||
test_storage_info()
|
||||
|
||||
print("\n测试完成!")
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from loguru import logger
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 移除默认处理器
|
||||
logger.remove()
|
||||
|
||||
# 添加控制台输出
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
|
||||
level="INFO",
|
||||
colorize=True
|
||||
)
|
||||
|
||||
print("=== Loguru Test ===")
|
||||
logger.info("This is an info message")
|
||||
logger.success("This is a success message")
|
||||
logger.warning("This is a warning message")
|
||||
logger.error("This is an error message")
|
||||
logger.debug("This debug message won't show (level too high)")
|
||||
|
||||
# 测试中文输出
|
||||
logger.info("中文测试输出 🚀")
|
||||
logger.success("上传文件成功 ✅")
|
||||
logger.error("下载文件失败 ❌")
|
||||
|
||||
print("Test completed!")
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from loguru import logger
|
||||
import sys
|
||||
|
||||
# 创建安全流处理器
|
||||
class SafeStreamHandler:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
|
||||
def write(self, message):
|
||||
try:
|
||||
self.stream.write(message)
|
||||
except UnicodeEncodeError:
|
||||
clean_message = message.encode('utf-8', errors='ignore').decode('utf-8')
|
||||
self.stream.write(clean_message)
|
||||
|
||||
def flush(self):
|
||||
self.stream.flush()
|
||||
|
||||
# 配置loguru
|
||||
logger.remove()
|
||||
logger.add(
|
||||
SafeStreamHandler(sys.stdout),
|
||||
format="{time:HH:mm:ss} | {level: <8} | {message}",
|
||||
level="INFO"
|
||||
)
|
||||
|
||||
print("=== Loguru Simple Test ===")
|
||||
logger.info("Server starting...")
|
||||
logger.info("Test upload file")
|
||||
logger.success("Upload completed")
|
||||
logger.warning("File already exists")
|
||||
logger.error("Download failed")
|
||||
print("Test completed!")
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
print("=== 测试输出 ===", flush=True)
|
||||
print("Python版本:", sys.version, flush=True)
|
||||
print("当前工作目录:", os.getcwd(), flush=True)
|
||||
print("stdout编码:", sys.stdout.encoding, flush=True)
|
||||
print("stderr编码:", sys.stderr.encoding, flush=True)
|
||||
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[logging.StreamHandler(sys.stdout)]
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info("这是一条测试日志")
|
||||
logger.warning("这是一条警告日志")
|
||||
|
||||
print("测试完成!", flush=True)
|
||||
|
||||
# 写入文件测试
|
||||
with open("test_log.txt", "w", encoding="utf-8") as f:
|
||||
f.write("测试文件写入\n")
|
||||
f.write(f"时间: {os.times()}\n")
|
||||
|
||||
print("文件写入完成", flush=True)
|
||||
@@ -1,308 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试POST文件API的脚本
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# API基础URL
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def create_test_user():
|
||||
"""创建测试用户"""
|
||||
register_data = {
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"password": "TestPass123!",
|
||||
"confirm_password": "TestPass123!"
|
||||
}
|
||||
|
||||
try:
|
||||
print("创建测试用户...")
|
||||
reg_response = requests.post(f"{BASE_URL}/auth/register", json=register_data)
|
||||
print(f"注册状态码: {reg_response.status_code}")
|
||||
|
||||
if reg_response.status_code == 201:
|
||||
reg_result = reg_response.json()
|
||||
if reg_result.get("success"):
|
||||
user_id = reg_result["data"]["user"]["id"]
|
||||
print(f"用户创建成功,ID: {user_id}")
|
||||
return user_id
|
||||
else:
|
||||
print(f"注册失败: {reg_result}")
|
||||
else:
|
||||
print(f"注册请求失败,状态码: {reg_response.status_code}")
|
||||
|
||||
# 如果注册失败,尝试获取现有用户
|
||||
print("尝试获取现有用户...")
|
||||
login_data = {
|
||||
"username": "testuser",
|
||||
"password": "TestPass123!"
|
||||
}
|
||||
login_response = requests.post(f"{BASE_URL}/auth/login", json=login_data)
|
||||
if login_response.status_code == 200:
|
||||
login_result = login_response.json()
|
||||
if login_result.get("success"):
|
||||
user_id = login_result["data"]["user"]["id"]
|
||||
print(f"登录成功,用户ID: {user_id}")
|
||||
return user_id
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"创建用户出错: {e}")
|
||||
return None
|
||||
|
||||
def test_file_upload(user_id):
|
||||
"""测试文件上传"""
|
||||
test_content = "Hello World! 测试文件上传内容"
|
||||
|
||||
try:
|
||||
files = {
|
||||
"file": ("test.txt", test_content.encode("utf-8"), "text/plain")
|
||||
}
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"description": "测试文件上传",
|
||||
"tags": "test,upload,api",
|
||||
"is_public": "false"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/upload",
|
||||
files=files,
|
||||
data=data
|
||||
)
|
||||
|
||||
print(f"上传状态码: {response.status_code}")
|
||||
print(f"上传响应: {response.text}")
|
||||
|
||||
if response.status_code == 201:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
print(f"上传成功! 文件ID: {file_info['id']}")
|
||||
print(f"文件名: {file_info['original_filename']}")
|
||||
print(f"文件大小: {file_info['file_size']} bytes")
|
||||
return file_info['id']
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"上传出错: {e}")
|
||||
return None
|
||||
|
||||
def test_file_list(user_id):
|
||||
"""测试获取文件列表"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"page": 1,
|
||||
"size": 20
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/list",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"列表状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
files = result["data"]["files"]
|
||||
pagination = result["data"]["pagination"]
|
||||
print(f"文件数量: {len(files)}")
|
||||
print(f"总数: {pagination['total']}")
|
||||
for f in files:
|
||||
print(f" - {f['original_filename']} ({f['file_size']} bytes)")
|
||||
print(f" ID: {f['id']}, 创建时间: {f['created_at']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取列表出错: {e}")
|
||||
|
||||
def test_file_info(user_id, file_id):
|
||||
"""测试获取文件信息"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/info",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"文件信息状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
info = result["data"]
|
||||
print(f"文件信息:")
|
||||
print(f" 文件名: {info['original_filename']}")
|
||||
print(f" 大小: {info['file_size']} bytes")
|
||||
print(f" MIME类型: {info['mime_type']}")
|
||||
print(f" 是否图片: {info['is_image']}")
|
||||
print(f" 是否文档: {info['is_document']}")
|
||||
print(f" 文件扩展名: {info['file_extension']}")
|
||||
print(f" 格式化大小: {info['size_formatted']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取文件信息出错: {e}")
|
||||
|
||||
def test_storage_info(user_id):
|
||||
"""测试存储信息"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/storage/info",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"存储信息状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
info = result["data"]
|
||||
print(f"存储信息:")
|
||||
print(f" 总配额: {info['total_quota']} bytes")
|
||||
print(f" 已使用: {info['used_space']} bytes")
|
||||
print(f" 可用空间: {info['available_space']} bytes")
|
||||
print(f" 使用百分比: {info['usage_percentage']:.2f}%")
|
||||
print(f" 文件数量: {info['file_count']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取存储信息出错: {e}")
|
||||
|
||||
def test_file_update(user_id, file_id):
|
||||
"""测试更新文件信息"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
update_data = {
|
||||
"description": "更新后的文件描述",
|
||||
"tags": "updated,test",
|
||||
"is_public": True
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/update",
|
||||
json=data,
|
||||
params=update_data
|
||||
)
|
||||
|
||||
print(f"更新文件状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
file_info = result["data"]["file"]
|
||||
print(f"文件更新成功:")
|
||||
print(f" 描述: {file_info['description']}")
|
||||
print(f" 标签: {file_info['tags']}")
|
||||
print(f" 是否公开: {file_info['is_public']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新文件出错: {e}")
|
||||
|
||||
def test_file_download(user_id, file_id):
|
||||
"""测试文件下载"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/download",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"下载状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print(f"下载成功! 内容长度: {len(response.content)} bytes")
|
||||
print(f"下载内容: {response.text[:100]}...") # 显示前100个字符
|
||||
else:
|
||||
print(f"下载失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"下载出错: {e}")
|
||||
|
||||
def test_file_delete(user_id, file_id):
|
||||
"""测试删除文件"""
|
||||
try:
|
||||
data = {
|
||||
"user_id": user_id,
|
||||
"file_id": file_id
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/files/delete",
|
||||
json=data
|
||||
)
|
||||
|
||||
print(f"删除状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
print("文件删除成功")
|
||||
else:
|
||||
print(f"删除失败: {result}")
|
||||
else:
|
||||
print(f"删除请求失败: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"删除文件出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("开始测试POST文件API...")
|
||||
|
||||
# 1. 创建测试用户
|
||||
user_id = create_test_user()
|
||||
if not user_id:
|
||||
print("无法创建或获取用户,退出测试")
|
||||
exit(1)
|
||||
|
||||
# 2. 上传文件
|
||||
print("\n=== 测试文件上传 ===")
|
||||
file_id = test_file_upload(user_id)
|
||||
if not file_id:
|
||||
print("文件上传失败,继续其他测试")
|
||||
|
||||
# 3. 获取文件列表
|
||||
print("\n=== 测试文件列表 ===")
|
||||
test_file_list(user_id)
|
||||
|
||||
# 4. 获取存储信息
|
||||
print("\n=== 测试存储信息 ===")
|
||||
test_storage_info(user_id)
|
||||
|
||||
# 5. 获取文件信息
|
||||
if file_id:
|
||||
print("\n=== 测试文件信息 ===")
|
||||
test_file_info(user_id, file_id)
|
||||
|
||||
# 6. 更新文件信息
|
||||
print("\n=== 测试文件更新 ===")
|
||||
test_file_update(user_id, file_id)
|
||||
|
||||
# 7. 下载文件
|
||||
print("\n=== 测试文件下载 ===")
|
||||
test_file_download(user_id, file_id)
|
||||
|
||||
# 8. 删除文件
|
||||
print("\n=== 测试文件删除 ===")
|
||||
test_file_delete(user_id, file_id)
|
||||
|
||||
print("\n测试完成!")
|
||||
@@ -1,116 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 测试服务器API连通性的脚本
|
||||
|
||||
echo "=== 测试云盘应用API ==="
|
||||
echo "目标地址: http://localhost:8080"
|
||||
echo "=================================="
|
||||
|
||||
# 检查服务器是否运行
|
||||
echo "1. 检查服务器状态..."
|
||||
|
||||
# 使用curl测试连接
|
||||
if curl -s http://localhost:8080/ > /dev/null; then
|
||||
echo "✅ 服务器正在运行"
|
||||
else
|
||||
echo "❌ 服务器未运行,请先启动服务器:"
|
||||
echo "cd /www/wwwroot/云盘后台/backend && python3 server_no_loguru.py"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "2. 测试API端点..."
|
||||
|
||||
# 测试根路径
|
||||
echo "测试根路径: http://localhost:8080/"
|
||||
root_response=$(curl -s http://localhost:8000/ 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 根路径响应正常"
|
||||
else
|
||||
echo "❌ 根路径无响应"
|
||||
fi
|
||||
|
||||
# 测试健康检查
|
||||
echo "测试健康检查: http://localhost:8080/api/v1/health"
|
||||
health_response=$(curl -s http://localhost:8080/api/v1/health 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ API健康检查正常"
|
||||
echo "响应内容: $health_response"
|
||||
else
|
||||
echo "❌ API健康检查无响应"
|
||||
fi
|
||||
|
||||
# 测试API文档
|
||||
echo "测试API文档: http://localhost:8080/docs"
|
||||
docs_response=$(curl -s -I http://localhost:8080/docs 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ API文档可访问"
|
||||
else
|
||||
echo "❌ API文档无法访问"
|
||||
fi
|
||||
|
||||
# 测试测试端点
|
||||
echo "测试端点: http://localhost:8080/test"
|
||||
test_response=$(curl -s http://localhost:8080/test 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 测试端点正常"
|
||||
echo "响应内容: $test_response"
|
||||
else
|
||||
echo "❌ 测试端点无响应"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "3. 详细测试..."
|
||||
|
||||
# 详细测试多个端点
|
||||
endpoints=(
|
||||
"http://localhost:8080/"
|
||||
"http://localhost:8080/health"
|
||||
"http://localhost:8080/api/v1/health"
|
||||
"http://localhost:8080/test"
|
||||
"http://localhost:8080/openapi.json"
|
||||
)
|
||||
|
||||
success_count=0
|
||||
total_count=${#endpoints[@]}
|
||||
|
||||
for endpoint in "${endpoints[@]}"; do
|
||||
echo -n "Testing $endpoint ... "
|
||||
|
||||
response=$(curl -s "$endpoint" 2>/dev/null)
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$endpoint" 2>/dev/null)
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
echo "✅ (200)"
|
||||
success_count=$((success_count + 1))
|
||||
else
|
||||
echo "❌ ($http_code)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=================================="
|
||||
echo "测试结果汇总:"
|
||||
echo "总测试数: $total_count"
|
||||
echo "成功: $success_count"
|
||||
echo "失败: $((total_count - success_count))"
|
||||
echo "成功率: $((success_count * 100 / total_count))%"
|
||||
|
||||
if [ $success_count -eq $total_count ]; then
|
||||
echo ""
|
||||
echo "🎉 所有API测试通过!"
|
||||
echo ""
|
||||
echo "📋 可访问的URL:"
|
||||
echo " http://localhost:8080/ (根路径)"
|
||||
echo " http://localhost:8080/docs (Swagger UI)"
|
||||
echo " http://localhost:8080/redoc (ReDoc)"
|
||||
echo " http://localhost:8080/api/v1/health (健康检查)"
|
||||
echo ""
|
||||
echo "✅ 服务器在端口8080上运行正常!"
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ 有 $((total_count - success_count)) 个测试失败"
|
||||
echo "请检查服务器状态和配置"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=================================="
|
||||
@@ -1,114 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
接口级上传下载测试
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import sys
|
||||
import io
|
||||
|
||||
# 设置stdout编码为utf-8
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
BASE_URL = "http://localhost:8000/api/v1"
|
||||
|
||||
def test_upload_download():
|
||||
print("=== 测试文件上传下载 ===")
|
||||
|
||||
# 1. 先创建测试用户
|
||||
print("1. 创建测试用户...")
|
||||
register_data = {
|
||||
"username": "testuser456",
|
||||
"email": "test456@example.com",
|
||||
"password": "Test123456!",
|
||||
"confirm_password": "Test123456!"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(f"{BASE_URL}/auth/register", json=register_data)
|
||||
print(f"注册响应状态: {response.status_code}")
|
||||
print(f"注册响应内容: {response.text}")
|
||||
|
||||
if response.status_code == 201:
|
||||
user_id = response.json()["data"]["user"]["id"]
|
||||
print(f"[OK] 用户创建成功,ID: {user_id}")
|
||||
else:
|
||||
# 创建另一个用户
|
||||
register_data = {
|
||||
"username": f"testuser{hash('test') % 10000}",
|
||||
"email": f"test{hash('test') % 10000}@example.com",
|
||||
"password": "Test123456!",
|
||||
"confirm_password": "Test123456!"
|
||||
}
|
||||
response = requests.post(f"{BASE_URL}/auth/register", json=register_data)
|
||||
if response.status_code == 201:
|
||||
user_id = response.json()["data"]["user"]["id"]
|
||||
print(f"[OK] 新用户创建成功,ID: {user_id}")
|
||||
else:
|
||||
print(f"[ERROR] 用户创建失败")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 请求异常: {e}")
|
||||
return
|
||||
|
||||
# 2. 上传文件
|
||||
print("\n2. 上传测试文件...")
|
||||
test_content = "这是测试文件内容\nTest file content\n你好世界!"
|
||||
|
||||
files = {'file': ('test.txt', test_content, 'text/plain')}
|
||||
data = {
|
||||
'user_id': str(user_id),
|
||||
'description': '测试文件',
|
||||
'tags': 'test',
|
||||
'is_public': 'false'
|
||||
}
|
||||
|
||||
try:
|
||||
upload_resp = requests.post(f"{BASE_URL}/files/upload", files=files, data=data)
|
||||
print(f"上传状态码: {upload_resp.status_code}")
|
||||
print(f"上传响应: {upload_resp.text}")
|
||||
|
||||
if upload_resp.status_code == 201:
|
||||
file_id = upload_resp.json()["data"]["file"]["id"]
|
||||
print(f"[OK] 上传成功,文件ID: {file_id}")
|
||||
else:
|
||||
print("[ERROR] 上传失败")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 上传异常: {e}")
|
||||
return
|
||||
|
||||
# 3. 下载文件
|
||||
print(f"\n3. 下载文件 ID: {file_id}...")
|
||||
download_data = {
|
||||
"file_id": file_id,
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
try:
|
||||
download_resp = requests.post(f"{BASE_URL}/files/download", json=download_data)
|
||||
print(f"下载状态码: {download_resp.status_code}")
|
||||
print(f"下载响应头: {dict(download_resp.headers)}")
|
||||
|
||||
if download_resp.status_code == 200:
|
||||
downloaded_content = download_resp.text
|
||||
print(f"[OK] 下载成功")
|
||||
print(f"下载内容: {downloaded_content}")
|
||||
print(f"内容长度: {len(downloaded_content)}")
|
||||
|
||||
# 验证内容
|
||||
if test_content == downloaded_content:
|
||||
print("[OK] 内容一致")
|
||||
else:
|
||||
print("[ERROR] 内容不一致")
|
||||
print(f"原始: {repr(test_content)}")
|
||||
print(f"下载: {repr(downloaded_content)}")
|
||||
else:
|
||||
print(f"[ERROR] 下载失败: {download_resp.text}")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 下载异常: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_upload_download()
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 云盘后端服务卸载脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SERVICE_NAME="cloud-drive"
|
||||
SERVICE_USER="cloud-drive"
|
||||
INSTALL_DIR="/opt/cloud-drive"
|
||||
|
||||
echo "=== 云盘后端服务卸载脚本 ==="
|
||||
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "错误: 请使用root权限运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 停止并禁用服务
|
||||
echo "停止服务..."
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
systemctl stop "$SERVICE_NAME"
|
||||
echo "✓ 服务已停止"
|
||||
fi
|
||||
|
||||
echo "禁用服务..."
|
||||
if systemctl is-enabled --quiet "$SERVICE_NAME"; then
|
||||
systemctl disable "$SERVICE_NAME"
|
||||
echo "✓ 服务已禁用"
|
||||
fi
|
||||
|
||||
# 删除systemd服务文件
|
||||
echo "删除systemd服务..."
|
||||
if [ -f "/etc/systemd/system/$SERVICE_NAME.service" ]; then
|
||||
rm -f "/etc/systemd/system/$SERVICE_NAME.service"
|
||||
systemctl daemon-reload
|
||||
echo "✓ systemd服务文件已删除"
|
||||
fi
|
||||
|
||||
# 删除防火墙规则
|
||||
echo "删除防火墙规则..."
|
||||
if command -v firewall-cmd &> /dev/null; then
|
||||
firewall-cmd --permanent --remove-port=8000/tcp 2>/dev/null || true
|
||||
firewall-cmd --reload 2>/dev/null || true
|
||||
echo "✓ 防火墙规则已删除 (firewalld)"
|
||||
elif command -v ufw &> /dev/null; then
|
||||
ufw delete allow 8000/tcp 2>/dev/null || true
|
||||
echo "✓ 防火墙规则已删除 (ufw)"
|
||||
fi
|
||||
|
||||
# 删除日志轮转配置
|
||||
echo "删除日志轮转配置..."
|
||||
if [ -f "/etc/logrotate.d/cloud-drive" ]; then
|
||||
rm -f "/etc/logrotate.d/cloud-drive"
|
||||
echo "✓ 日志轮转配置已删除"
|
||||
fi
|
||||
|
||||
# 询问是否删除数据
|
||||
echo ""
|
||||
read -p "是否删除所有数据和配置文件?(y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# 删除安装目录
|
||||
echo "删除安装目录..."
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
echo "✓ 安装目录已删除"
|
||||
fi
|
||||
|
||||
# 删除服务用户
|
||||
echo "删除服务用户..."
|
||||
if id "$SERVICE_USER" &>/dev/null; then
|
||||
userdel "$SERVICE_USER" 2>/dev/null || true
|
||||
echo "✓ 服务用户已删除"
|
||||
fi
|
||||
|
||||
echo "✓ 所有数据已删除"
|
||||
else
|
||||
echo "保留数据目录: $INSTALL_DIR"
|
||||
echo "如需手动删除,请运行: rm -rf $INSTALL_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 卸载完成 ==="
|
||||
echo "云盘后端服务已从系统中完全移除"
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
print("🚀 启动云盘应用服务器...")
|
||||
|
||||
# 检查Python环境
|
||||
import sys
|
||||
print(f"Python版本: {sys.version}")
|
||||
|
||||
# 尝试导入FastAPI
|
||||
try:
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import uvicorn
|
||||
print("✅ FastAPI依赖可用")
|
||||
|
||||
# 创建FastAPI应用
|
||||
app = FastAPI(
|
||||
title="云盘应用 API",
|
||||
description="现代化的云存储Web应用后端API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
# 添加CORS中间件
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 定义路由
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"message": "云盘应用 API",
|
||||
"version": "1.0.1",
|
||||
"docs": "/docs",
|
||||
"health": "/api/v1/health"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "healthy"}
|
||||
|
||||
@app.get("/api/v1/health")
|
||||
async def api_health():
|
||||
import time
|
||||
return {
|
||||
"status": "healthy",
|
||||
"timestamp": time.time(),
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@app.get("/test")
|
||||
async def test():
|
||||
return {"test": "ok", "server": "working"}
|
||||
|
||||
# 尝试导入app模块以提供完整功能
|
||||
try:
|
||||
from app.core.config import settings
|
||||
print("✅ 完整app模块可用")
|
||||
mode = "full"
|
||||
except ImportError:
|
||||
print("⚠️ 使用简化模式")
|
||||
mode = "simplified"
|
||||
|
||||
@app.get("/info")
|
||||
async def info():
|
||||
return {
|
||||
"mode": mode,
|
||||
"python_version": str(sys.version),
|
||||
"status": "running"
|
||||
}
|
||||
|
||||
print("=" * 50)
|
||||
print("📍 服务器地址:")
|
||||
print(" http://localhost:8080")
|
||||
print(" http://localhost:8080/docs")
|
||||
print(" http://localhost:8080/api/v1/health")
|
||||
print("=" * 50)
|
||||
print("按 Ctrl+C 停止服务")
|
||||
print()
|
||||
|
||||
# 启动服务器
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
reload=False,
|
||||
access_log=True,
|
||||
log_level="info"
|
||||
)
|
||||
|
||||
except ImportError as e:
|
||||
print(f"❌ 依赖导入失败: {e}")
|
||||
print("请运行: pip install fastapi uvicorn")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user