build: 打通 FastAPI 与 Vue 最小工程、依赖与数据库连接
后端入口、配置、Redis 与 Alembic 迁移就位;前端 Vite 工程可安装可构建。
This commit is contained in:
1 parent
fb0ddca4de
commit
2aee6286d6
167 files changed
+26082
No files matched your search
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Copyright 2024-2026 ChatterMate
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.exception_handlers import http_exception_handler
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.responses import Response
|
||||
|
||||
from app.core.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# What a 5xx answers with instead of the server's own error text.
|
||||
INTERNAL_ERROR_DETAIL = "Internal server error"
|
||||
|
||||
|
||||
class ClientSafeHTTPException(HTTPException):
|
||||
"""A 5xx whose detail is meant for the caller and holds nothing internal.
|
||||
|
||||
Raise this for a message we wrote ourselves, or an upstream provider's own
|
||||
wording that the operator has to act on — "Recipient not on WhatsApp" is
|
||||
useless in a log and essential on screen. Every other 5xx is assumed to be
|
||||
carrying server internals and has its detail replaced, which is what makes
|
||||
the widespread `detail=str(e)` safe by default.
|
||||
"""
|
||||
|
||||
|
||||
async def sanitized_http_exception_handler(
|
||||
request: Request, exc: StarletteHTTPException
|
||||
) -> Response:
|
||||
"""Answer 5xx with a fixed message and keep the real one in the log.
|
||||
|
||||
Routes across the API raise `HTTPException(500, detail=str(e))`, which hands
|
||||
the caller whatever the exception happened to carry — SQL fragments, file
|
||||
paths, connection strings. None of it is actionable for a client, and a 5xx
|
||||
means the fault is ours, so the body gets a constant and the original goes to
|
||||
the log together with the route that produced it.
|
||||
|
||||
4xx detail is written for the caller ("Agent not found", "Invalid token") and
|
||||
is passed through untouched, as is any `ClientSafeHTTPException`.
|
||||
"""
|
||||
if exc.status_code < 500 or isinstance(exc, ClientSafeHTTPException):
|
||||
return await http_exception_handler(request, exc)
|
||||
|
||||
logger.error(
|
||||
f"{request.method} {request.url.path} -> {exc.status_code}: {exc.detail}"
|
||||
)
|
||||
return await http_exception_handler(
|
||||
request,
|
||||
StarletteHTTPException(
|
||||
status_code=exc.status_code,
|
||||
detail=INTERNAL_ERROR_DETAIL,
|
||||
headers=exc.headers,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def install_error_handlers(app: FastAPI) -> None:
|
||||
"""Register the sanitizer. Called for every FastAPI instance we serve."""
|
||||
app.add_exception_handler(StarletteHTTPException, sanitized_http_exception_handler)
|
||||
Reference in new issue
Block a user