fix header

This commit is contained in:
felix
2025-11-23 10:33:00 +08:00
parent 0d871644f0
commit de780d783a
6 changed files with 106 additions and 21 deletions

19
.dockerignore Normal file
View File

@@ -0,0 +1,19 @@
__pycache__
*.pyo
*.pyd
.Python
env
venv
.venv
.env
.git
.cache
.pytest_cache
.coverage
htmlcov
*.log
notebooks
.DS_Store
README.md
.dockerignore
Dockerfile

View File

@@ -1,29 +1,60 @@
# 使用多阶段构建来分离构建环境和运行环境
FROM python:3.10-slim as builder
WORKDIR /app
# 1. 先只复制依赖文件
COPY requirements.txt .
# 换源和安装构建依赖
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources \
&& sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources
# 安装构建依赖
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc python3-dev \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple \
&& pip install --user -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
# 第二阶段:运行环境
FROM python:3.10-slim FROM python:3.10-slim
WORKDIR /app WORKDIR /app
COPY . . # 换源
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources \ RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources \
&& sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources && sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources
# 安装运行时的系统依赖(只安装必要的)
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc python3-dev supervisor \ && apt-get install -y --no-install-recommends supervisor \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/*
&& pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple \
&& pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple \
&& pip install gunicorn wait-for-it -i https://mirrors.aliyun.com/pypi/simple
# 从构建阶段复制已安装的Python包
COPY --from=builder /root/.local /root/.local
# 设置PATH让Python可以找到用户安装的包
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONPATH=/app
# 设置时区
ENV TZ="Asia/Shanghai" ENV TZ="Asia/Shanghai"
RUN mkdir -p /var/log/fastapi_server \ # 创建目录结构
RUN mkdir -p /var/log/app_server \
&& mkdir -p /var/log/supervisor \ && mkdir -p /var/log/supervisor \
&& mkdir -p /etc/supervisor/conf.d && mkdir -p /etc/supervisor/conf.d
# 复制配置文件
COPY deploy/supervisor.conf /etc/supervisor/supervisord.conf COPY deploy/supervisor.conf /etc/supervisor/supervisord.conf
COPY deploy/app_server.conf /etc/supervisor/conf.d/
COPY deploy/fastapi_server.conf /etc/supervisor/conf.d/ # 最后复制应用代码利用Docker缓存层
COPY backend/ ./backend
EXPOSE 8001 EXPOSE 8001
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8080"] CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8080"]

View File

@@ -4,8 +4,6 @@ import logging
import asyncio import asyncio
from typing import Optional, List from typing import Optional, List
from soupsieve.util import lower
from backend.app.ai.crud import recording_dao from backend.app.ai.crud import recording_dao
from backend.app.ai.crud.image_text_crud import image_text_dao from backend.app.ai.crud.image_text_crud import image_text_dao
from backend.app.ai.model.image_text import ImageText from backend.app.ai.model.image_text import ImageText

View File

@@ -10,9 +10,8 @@ from datetime import datetime
from backend.app.ai.crud.image_task_crud import image_task_dao from backend.app.ai.crud.image_task_crud import image_task_dao
from backend.app.ai.model.image_task import ImageTaskStatus from backend.app.ai.model.image_task import ImageTaskStatus
from backend.app.ai.service.image_service import image_service from backend.app.ai.service.image_service import image_service
from backend.database.db import async_db_session, background_db_session from backend.database.db import background_db_session
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError, OperationalError
from asyncpg import SerializationError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -85,8 +84,8 @@ async def update_task_status_with_retry(db, task_id, status, result=None, error_
try: try:
result = await image_task_dao.update_task_status(db, task_id, status, result, error_message) result = await image_task_dao.update_task_status(db, task_id, status, result, error_message)
return result return result
except SerializationError as e: except OperationalError as e:
logger.warning(f"Serialization error updating task {task_id} status to {status} (attempt {attempt + 1}): {str(e)}") logger.warning(f"Operational error updating task {task_id} status to {status} (attempt {attempt + 1}): {str(e)}")
if attempt < max_retries - 1: if attempt < max_retries - 1:
# Exponential backoff with jitter # Exponential backoff with jitter
import random import random
@@ -116,8 +115,8 @@ async def increment_retry_count_with_retry(db, task_id, max_retries=3):
try: try:
result = await image_task_dao.increment_retry_count(db, task_id) result = await image_task_dao.increment_retry_count(db, task_id)
return result return result
except SerializationError as e: except OperationalError as e:
logger.warning(f"Serialization error incrementing retry count for task {task_id} (attempt {attempt + 1}): {str(e)}") logger.warning(f"Operational error incrementing retry count for task {task_id} (attempt {attempt + 1}): {str(e)}")
if attempt < max_retries - 1: if attempt < max_retries - 1:
# Exponential backoff with jitter # Exponential backoff with jitter
import random import random

View File

@@ -17,7 +17,9 @@ from pydantic_core import from_json
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from backend.app.admin.model import WxUser from backend.app.admin.model import WxUser
from backend.app.admin.schema.wx import GetWxUserInfoWithRelationDetail from backend.app.admin.model.dict import DictCategory
from backend.app.admin.schema.wx import GetWxUserInfoWithRelationDetail, DictLevel
from backend.app.admin.service.points_service import points_service
from backend.common.dataclasses import AccessToken, NewToken, RefreshToken, TokenPayload from backend.common.dataclasses import AccessToken, NewToken, RefreshToken, TokenPayload
from backend.common.exception import errors from backend.common.exception import errors
from backend.common.exception.errors import TokenError from backend.common.exception.errors import TokenError
@@ -25,6 +27,7 @@ from backend.core.conf import settings
from backend.database.db import async_db_session from backend.database.db import async_db_session
from backend.database.redis import redis_client from backend.database.redis import redis_client
from backend.utils.serializers import select_as_dict from backend.utils.serializers import select_as_dict
from backend.utils.snowflake import snowflake
from backend.utils.timezone import timezone from backend.utils.timezone import timezone
@@ -337,3 +340,31 @@ async def jwt_authentication(token: str) -> GetWxUserInfoWithRelationDetail:
# https://docs.pydantic.dev/latest/concepts/json/#partial-json-parsing # https://docs.pydantic.dev/latest/concepts/json/#partial-json-parsing
user = GetWxUserInfoWithRelationDetail.model_validate(from_json(cache_user, allow_partial=True)) user = GetWxUserInfoWithRelationDetail.model_validate(from_json(cache_user, allow_partial=True))
return user return user
async def wx_openid_authentication(openid: str, unionid: str) -> GetWxUserInfoWithRelationDetail:
from backend.app.admin.crud.wx_user_crud import wx_user_dao
async with async_db_session() as db:
user = None
try:
# 查找或创建用户
user = await wx_user_dao.get_by_openid(db, openid)
if not user:
session_key = snowflake.generate()
user = WxUser(
openid=openid,
unionid=unionid,
session_key=session_key,
profile={
'dict_level': DictLevel.LEVEL1.value,
'dict_category': DictCategory.GENERAL.value
},
)
await wx_user_dao.add(db, user)
await db.flush()
await db.refresh(user)
await points_service.initialize_user_points(user_id=user.id, db=db)
return GetWxUserInfoWithRelationDetail(**select_as_dict(user))
except Exception as e:
db.rollback()
raise

View File

@@ -10,7 +10,7 @@ from starlette.requests import HTTPConnection
from backend.app.admin.schema.wx import GetWxUserInfoWithRelationDetail from backend.app.admin.schema.wx import GetWxUserInfoWithRelationDetail
from backend.common.exception.errors import TokenError from backend.common.exception.errors import TokenError
from backend.common.log import log from backend.common.log import log
from backend.common.security.jwt import jwt_authentication from backend.common.security.jwt import jwt_authentication, wx_openid_authentication
from backend.core.conf import settings from backend.core.conf import settings
from backend.utils.serializers import MsgSpecJSONResponse from backend.utils.serializers import MsgSpecJSONResponse
@@ -55,6 +55,13 @@ class JwtAuthMiddleware(AuthenticationBackend):
:param request: FastAPI 请求对象 :param request: FastAPI 请求对象
:return: :return:
""" """
wx_openid = request.headers.get('X-WX-OPENID')
wx_unionid = request.headers.get('X-WX-UNIONID')
if wx_openid:
user = await wx_openid_authentication(wx_openid, wx_unionid)
if user:
return AuthCredentials(['authenticated']), user
token = request.headers.get('Authorization') token = request.headers.get('Authorization')
if not token: if not token:
return None return None