重写独立dockerfile
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# ============================================
|
||||
# .dockerignore — 排除无关文件,加速构建
|
||||
# ============================================
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Virtual environments
|
||||
.env
|
||||
venv/
|
||||
env/
|
||||
.conda/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# 前端(仅在需要构建时包含)
|
||||
frontend/node_modules/
|
||||
frontend/dist/
|
||||
|
||||
# 测试
|
||||
tests/
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# 文档
|
||||
docs/
|
||||
*.md
|
||||
README*
|
||||
|
||||
# 临时文件
|
||||
*.log
|
||||
logs/
|
||||
temp/
|
||||
tmp/
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
docker-compose*.yml
|
||||
deploy/
|
||||
|
||||
# 其他
|
||||
.trae/
|
||||
scripts/
|
||||
pip_audit_local.json
|
||||
temp_requirements_audit.txt
|
||||
local_src.txt
|
||||
git_src.txt
|
||||
start.sh
|
||||
CHANGELOG.md
|
||||
LICENSE
|
||||
@@ -0,0 +1,64 @@
|
||||
# ============================================
|
||||
# 环境变量模板 — 独立部署版
|
||||
# ============================================
|
||||
# 复制为 .env 并修改配置
|
||||
|
||||
# 数据库
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=moldinsight
|
||||
DB_USER=moldinsight_user
|
||||
DB_PASSWORD=moldinsight_password
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# MinIO (RustFS 兼容存储)
|
||||
MINIO_ACCESS_KEY=minioadmin
|
||||
MINIO_SECRET_KEY=minioadmin
|
||||
|
||||
# 认证
|
||||
SECRET_KEY=your-secret-key-change-in-production-min-32-chars
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
||||
|
||||
# 管理员
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=change-this-to-a-secure-password
|
||||
ADMIN_EMAIL=admin@gemold.com
|
||||
ADMIN_FULL_NAME=系统管理员
|
||||
|
||||
# 端口
|
||||
MOLDINSIGHT_PORT=8000
|
||||
INVENTORY_PORT=8001
|
||||
|
||||
# 文件上传
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=104857600
|
||||
ALLOWED_EXTENSIONS=.stp,.step,.stp.gz
|
||||
|
||||
# 几何处理
|
||||
POINTCLOUD_SAMPLE_COUNT=10000
|
||||
MESH_QUALITY=high
|
||||
PARALLEL_PROCESSING=true
|
||||
|
||||
# RustFS 对象存储
|
||||
RUSTFS_ENDPOINT=http://minio:9000
|
||||
RUSTFS_ACCESS_KEY=minioadmin
|
||||
RUSTFS_SECRET_KEY=minioadmin
|
||||
RUSTFS_TIMEOUT=30
|
||||
RUSTFS_PRESIGNED_URL_EXPIRES=3600
|
||||
|
||||
# LLM (可选)
|
||||
LLM_ENABLED=false
|
||||
LLM_API_URL=https://api.openai.com/v1
|
||||
LLM_API_KEY=sk-your-api-key
|
||||
LLM_MODEL=gpt-4o-mini
|
||||
LLM_TIMEOUT=60
|
||||
LLM_MAX_TOKENS=2000
|
||||
|
||||
# FreeCAD 验证 (可选)
|
||||
ENABLE_FREECAD_VERIFICATION=false
|
||||
FREECAD_VERIFICATION_TIMEOUT=120
|
||||
@@ -0,0 +1,17 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY deploy/requirements-base.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements-base.txt && \
|
||||
rm requirements-base.txt
|
||||
|
||||
COPY src/shared/ /app/src/shared/
|
||||
|
||||
ENV PYTHONPATH=/app/src
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM gemold-moldinsight:latest
|
||||
|
||||
CMD ["celery", "-A", "celery_app", "worker", "--workdir=/app/src", "--concurrency=2", "--loglevel=info"]
|
||||
@@ -0,0 +1,14 @@
|
||||
FROM gemold-base:latest
|
||||
|
||||
COPY src/inventory/ /app/src/inventory/
|
||||
COPY src/entrypoints/ /app/src/entrypoints/
|
||||
COPY static/ /app/static/
|
||||
|
||||
RUN mkdir -p /app/logs
|
||||
|
||||
EXPOSE 8001
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=30s \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8001/health')" || exit 1
|
||||
|
||||
CMD ["python", "-m", "uvicorn", "entrypoints.inventory:app", "--host", "0.0.0.0", "--port", "8001"]
|
||||
@@ -0,0 +1,30 @@
|
||||
FROM continuumio/miniconda3:latest AS pythonocc
|
||||
|
||||
RUN conda update -n base -c defaults conda -y && \
|
||||
conda create -n moldinsight python=3.11 pythonocc-core=7.9.0 -c conda-forge -y
|
||||
|
||||
FROM gemold-base:latest
|
||||
|
||||
COPY --from=pythonocc /opt/conda/envs/moldinsight/lib/python3.11/site-packages/ /usr/local/lib/python3.11/site-packages/
|
||||
|
||||
COPY deploy/requirements-moldinsight.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements-moldinsight.txt && \
|
||||
rm requirements-moldinsight.txt
|
||||
|
||||
COPY src/moldinsight/ /app/src/moldinsight/
|
||||
COPY src/inventory/ /app/src/inventory/
|
||||
COPY src/entrypoints/ /app/src/entrypoints/
|
||||
COPY src/celery_app.py src/celery_tasks.py /app/src/
|
||||
COPY uploads/ /app/uploads/
|
||||
COPY static/ /app/static/
|
||||
COPY html_output/ /app/html_output/
|
||||
|
||||
RUN mkdir -p /app/logs
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=60s \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
|
||||
|
||||
CMD ["python", "-m", "uvicorn", "entrypoints.moldinsight:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -0,0 +1,32 @@
|
||||
@echo off
|
||||
REM ============================================
|
||||
REM 构建脚本 — 独立部署 (Windows)
|
||||
REM ============================================
|
||||
cd /d "%~dp0\.."
|
||||
|
||||
echo === 构建基础镜像 ===
|
||||
docker build -t gemold-base:latest -f deploy\Dockerfile.base .
|
||||
|
||||
echo.
|
||||
echo === 构建 MoldInsight 镜像 (含 PythonOCC) ===
|
||||
docker build -t gemold-moldinsight:latest -f deploy\Dockerfile.moldinsight .
|
||||
|
||||
echo.
|
||||
echo === 构建 Inventory 镜像 ===
|
||||
docker build -t gemold-inventory:latest -f deploy\Dockerfile.inventory .
|
||||
|
||||
echo.
|
||||
echo === 构建 Celery Worker 镜像 ===
|
||||
docker build -t gemold-celery:latest -f deploy\Dockerfile.celery .
|
||||
|
||||
echo.
|
||||
echo === 全部构建完成 ===
|
||||
echo.
|
||||
echo 启动完整系统:
|
||||
echo cd deploy ^&^& docker compose --profile full up -d
|
||||
echo.
|
||||
echo 仅启动进销存:
|
||||
echo cd deploy ^&^& docker compose --profile inventory up -d
|
||||
echo.
|
||||
echo 仅启动模具分析:
|
||||
echo cd deploy ^&^& docker compose --profile moldinsight up -d
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# 构建脚本 — 独立部署
|
||||
# ============================================
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "=== 构建基础镜像 ==="
|
||||
docker build -t gemold-base:latest -f deploy/Dockerfile.base .
|
||||
|
||||
echo ""
|
||||
echo "=== 构建 MoldInsight 镜像 (含 PythonOCC) ==="
|
||||
docker build -t gemold-moldinsight:latest -f deploy/Dockerfile.moldinsight .
|
||||
|
||||
echo ""
|
||||
echo "=== 构建 Inventory 镜像 ==="
|
||||
docker build -t gemold-inventory:latest -f deploy/Dockerfile.inventory .
|
||||
|
||||
echo ""
|
||||
echo "=== 构建 Celery Worker 镜像 ==="
|
||||
docker build -t gemold-celery:latest -f deploy/Dockerfile.celery .
|
||||
|
||||
echo ""
|
||||
echo "=== 全部构建完成 ==="
|
||||
echo ""
|
||||
echo "启动完整系统:"
|
||||
echo " cd deploy && docker compose --profile full up -d"
|
||||
echo ""
|
||||
echo "仅启动进销存:"
|
||||
echo " cd deploy && docker compose --profile inventory up -d"
|
||||
echo ""
|
||||
echo "仅启动模具分析:"
|
||||
echo " cd deploy && docker compose --profile moldinsight up -d"
|
||||
@@ -0,0 +1,163 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
container_name: gemold_postgres
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME:-moldinsight}
|
||||
POSTGRES_USER: ${DB_USER:-moldinsight_user}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-moldinsight_password}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-moldinsight_user} -d ${DB_NAME:-moldinsight}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: gemold_redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: gemold_minio
|
||||
command: server /data --console-address ":9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
moldinsight:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile.moldinsight
|
||||
image: gemold-moldinsight:latest
|
||||
container_name: gemold_moldinsight
|
||||
ports:
|
||||
- "${MOLDINSIGHT_PORT:-8000}:8000"
|
||||
environment:
|
||||
HOST: 0.0.0.0
|
||||
PORT: "8000"
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: ${DB_NAME:-moldinsight}
|
||||
DB_USER: ${DB_USER:-moldinsight_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-moldinsight_password}
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
RUSTFS_ENDPOINT: http://minio:9000
|
||||
RUSTFS_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
|
||||
RUSTFS_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
|
||||
SECRET_KEY: ${SECRET_KEY:-change-me-in-production}
|
||||
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- full
|
||||
- moldinsight
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
moldinsight-celery:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile.celery
|
||||
container_name: gemold_celery
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: ${DB_NAME:-moldinsight}
|
||||
DB_USER: ${DB_USER:-moldinsight_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-moldinsight_password}
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
RUSTFS_ENDPOINT: http://minio:9000
|
||||
RUSTFS_ACCESS_KEY: ${MINIO_ACCESS_KEY:-minioadmin}
|
||||
RUSTFS_SECRET_KEY: ${MINIO_SECRET_KEY:-minioadmin}
|
||||
depends_on:
|
||||
- moldinsight
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- full
|
||||
- moldinsight
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
inventory:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile.inventory
|
||||
image: gemold-inventory:latest
|
||||
container_name: gemold_inventory
|
||||
ports:
|
||||
- "${INVENTORY_PORT:-8001}:8001"
|
||||
environment:
|
||||
HOST: 0.0.0.0
|
||||
PORT: "8001"
|
||||
DB_HOST: postgres
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: ${DB_NAME:-moldinsight}
|
||||
DB_USER: ${DB_USER:-moldinsight_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-moldinsight_password}
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
SECRET_KEY: ${SECRET_KEY:-change-me-in-production}
|
||||
ADMIN_USERNAME: ${ADMIN_USERNAME:-admin}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
- full
|
||||
- inventory
|
||||
networks:
|
||||
- gemold_network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
minio_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
gemold_network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,30 @@
|
||||
-i https://mirrors.aliyun.com/pypi/simple/
|
||||
--trusted-host mirrors.aliyun.com
|
||||
|
||||
fastapi>=0.100.0
|
||||
uvicorn[standard]>=0.22.0
|
||||
pydantic>=2.0.0
|
||||
python-multipart>=0.0.6
|
||||
|
||||
sqlalchemy>=2.0.0
|
||||
psycopg2-binary>=2.9.0
|
||||
asyncpg>=0.28.0
|
||||
alembic>=1.11.0
|
||||
|
||||
python-jose[cryptography]>=3.3.0
|
||||
bcrypt>=4.0.0
|
||||
passlib>=1.7.4
|
||||
email-validator>=2.0.0
|
||||
|
||||
aiofiles>=23.0.0
|
||||
orjson>=3.9.0
|
||||
python-dotenv>=1.0.0
|
||||
jinja2>=3.1.0
|
||||
Pillow>=12.2.0
|
||||
pyyaml>=6.0
|
||||
python-dateutil>=2.8.0
|
||||
|
||||
loguru>=0.7.0
|
||||
|
||||
httpx>=0.24.0
|
||||
redis>=4.5.0
|
||||
@@ -0,0 +1,20 @@
|
||||
-i https://mirrors.aliyun.com/pypi/simple/
|
||||
--trusted-host mirrors.aliyun.com
|
||||
|
||||
trimesh>=3.21.0
|
||||
numpy>=1.24.0
|
||||
scipy>=1.10.0
|
||||
pyvista>=0.38.0
|
||||
|
||||
minio>=7.1.0
|
||||
aiohttp>=3.13.4
|
||||
|
||||
celery[redis]>=5.3.0
|
||||
kafka-python>=2.0.2
|
||||
|
||||
pytest>=7.0.0
|
||||
pytest-asyncio>=0.21.0
|
||||
aiosqlite>=0.19.0
|
||||
black>=23.0.0
|
||||
flake8>=6.0.0
|
||||
mypy>=1.0.0
|
||||
@@ -0,0 +1,78 @@
|
||||
import os, sys
|
||||
from pathlib import Path
|
||||
|
||||
src_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(src_root))
|
||||
|
||||
os.chdir(Path(__file__).parent.parent.parent)
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse, FileResponse
|
||||
import asyncio, time
|
||||
|
||||
from shared.config.settings import settings
|
||||
from shared.services.auth_routes import router as auth_router
|
||||
from shared.utils.logger import setup_logging, get_logger
|
||||
from shared.database.init_db import init_database
|
||||
|
||||
setup_logging()
|
||||
logger = get_logger(__name__)
|
||||
|
||||
app = FastAPI(title="Gemold - 进销存管理系统", version="4.0.0")
|
||||
|
||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
||||
|
||||
@app.middleware("http")
|
||||
async def log_requests(request: Request, call_next):
|
||||
start_time = time.time()
|
||||
response = await call_next(request)
|
||||
duration = time.time() - start_time
|
||||
if response.status_code >= 400:
|
||||
logger.warning(f"[HTTP] {request.method} {request.url.path} -> {response.status_code} ({duration:.2f}s)")
|
||||
return response
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
success = await init_database(keep_connected=True)
|
||||
print(f"[{'OK' if success else 'FAIL'}] 数据库初始化")
|
||||
try:
|
||||
from shared.services.redis_task_manager import redis_task_manager
|
||||
await redis_task_manager.connect()
|
||||
print(f"[{'OK' if redis_task_manager.is_connected else 'WARN'}] Redis")
|
||||
except Exception as e:
|
||||
print(f"[WARN] Redis异常: {e}")
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
try:
|
||||
from shared.services.redis_task_manager import redis_task_manager
|
||||
await redis_task_manager.disconnect()
|
||||
except: pass
|
||||
|
||||
app.mount("/static", StaticFiles(directory=os.path.join(os.getcwd(), "static")), name="static")
|
||||
|
||||
app.include_router(auth_router)
|
||||
|
||||
try:
|
||||
from inventory.api import inventory_router
|
||||
app.include_router(inventory_router)
|
||||
except Exception as e:
|
||||
print(f"[WARN] Inventory 路由: {e}")
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
from shared.database.database import db_manager
|
||||
from sqlalchemy import text
|
||||
db_ok = False; db_error = None
|
||||
try:
|
||||
if not db_manager.is_connected: await db_manager.connect()
|
||||
async with db_manager.engine.begin() as conn: await conn.execute(text("SELECT 1"))
|
||||
db_ok = True
|
||||
except Exception as e: db_error = str(e)
|
||||
return {"status": "healthy", "service": "inventory", "version": "4.0.0", "database_connected": db_ok, "database_error": db_error}
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def spa_fallback(full_path: str):
|
||||
return FileResponse(os.path.join(os.getcwd(), "static", "index.html"))
|
||||
@@ -0,0 +1,88 @@
|
||||
import os, sys
|
||||
from pathlib import Path
|
||||
|
||||
src_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(src_root))
|
||||
|
||||
os.chdir(Path(__file__).parent.parent.parent)
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse, FileResponse
|
||||
import asyncio, time
|
||||
|
||||
from shared.config.settings import settings
|
||||
from shared.services.auth_routes import router as auth_router
|
||||
from shared.utils.logger import setup_logging, get_logger
|
||||
from shared.database.init_db import init_database
|
||||
|
||||
setup_logging()
|
||||
logger = get_logger(__name__)
|
||||
|
||||
app = FastAPI(title="Gemold - 模具分析引擎", version="4.0.0")
|
||||
|
||||
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
||||
|
||||
@app.middleware("http")
|
||||
async def log_requests(request: Request, call_next):
|
||||
start_time = time.time()
|
||||
response = await call_next(request)
|
||||
duration = time.time() - start_time
|
||||
if response.status_code >= 400:
|
||||
logger.warning(f"[HTTP] {request.method} {request.url.path} -> {response.status_code} ({duration:.2f}s)")
|
||||
return response
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
success = await init_database(keep_connected=True)
|
||||
print(f"[{'OK' if success else 'FAIL'}] 数据库初始化")
|
||||
try:
|
||||
from moldinsight.storage.rustfs_storage import rustfs_manager
|
||||
await rustfs_manager.connect(endpoint=settings.RUSTFS_ENDPOINT, access_key=settings.RUSTFS_ACCESS_KEY, secret_key=settings.RUSTFS_SECRET_KEY, timeout=settings.RUSTFS_TIMEOUT)
|
||||
print("[OK] RustFS连接成功")
|
||||
except Exception as e:
|
||||
print(f"[WARN] RustFS连接失败: {e}")
|
||||
try:
|
||||
from shared.services.redis_task_manager import redis_task_manager
|
||||
await redis_task_manager.connect()
|
||||
print(f"[{'OK' if redis_task_manager.is_connected else 'WARN'}] Redis")
|
||||
except Exception as e:
|
||||
print(f"[WARN] Redis异常: {e}")
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
try:
|
||||
from shared.services.redis_task_manager import redis_task_manager
|
||||
await redis_task_manager.disconnect()
|
||||
except: pass
|
||||
|
||||
UPLOAD_DIR = Path("uploads"); UPLOAD_DIR.mkdir(exist_ok=True)
|
||||
Path("html_output").mkdir(exist_ok=True)
|
||||
|
||||
app.mount("/static", StaticFiles(directory=os.path.join(os.getcwd(), "static")), name="static")
|
||||
app.mount("/html", StaticFiles(directory=os.path.join(os.getcwd(), "html_output")), name="html")
|
||||
|
||||
app.include_router(auth_router)
|
||||
|
||||
try:
|
||||
from moldinsight.api import router as moldinsight_router
|
||||
app.include_router(moldinsight_router, prefix="/api")
|
||||
except Exception as e:
|
||||
print(f"[WARN] MoldInsight 路由: {e}")
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
from shared.database.database import db_manager
|
||||
from sqlalchemy import text
|
||||
db_ok = False; db_error = None
|
||||
try:
|
||||
if not db_manager.is_connected: await db_manager.connect()
|
||||
async with db_manager.engine.begin() as conn: await conn.execute(text("SELECT 1"))
|
||||
db_ok = True
|
||||
except Exception as e: db_error = str(e)
|
||||
return {"status": "healthy", "service": "moldinsight", "version": "4.0.0", "database_connected": db_ok, "database_error": db_error}
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def spa_fallback(full_path: str):
|
||||
return FileResponse(os.path.join(os.getcwd(), "static", "index.html"))
|
||||
Reference in New Issue
Block a user