Commit 4ffe4159 authored by Data Governance Dev's avatar Data Governance Dev

feat(web3): 后端骨架 + Vite 代理 + start.bat 同窗口启动

- 新增 web3/backend/ 骨架(FastAPI + SQLAlchemy 2 + SQLite)
  - start.py / app.py / _logging.py / config.py / requirements.txt
  - db/database.py:engine + SessionLocal + get_session(init_db 占位)
  - 空的 models/__init__.py、routers/__init__.py、db/__init__.py
- vite.config.js 加 /api 代理 → http://localhost:8767
- web3/start.bat 修复:cwd 保持在项目根,python -m web3.backend.start 能 import web3 包
  - 后端启动后 curl /api/health 轮询 20×1s,就绪再起前端
  - REM 中文 + 等号改纯文本,ping 替代 timeout,LF→CRLF 转换
parent e8512305
"""web3 后端统一日志配置
从 web2/backend/_logging.py 拷贝改造(路径前缀 web2 → web3)。
- 重复调用幂等
- 所有 web3.backend.* 日志同时写 stdout + app.log
- 不接管 root / web.core.* logger
"""
from __future__ import annotations
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
# ── 路径常量 ──
WEB3_DIR = Path(__file__).resolve().parent
LOG_DIR = WEB3_DIR / "logs"
LOG_DIR.mkdir(parents=True, exist_ok=True)
APP_LOG_FILE = LOG_DIR / "app.log"
_LOG_FORMAT = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
_DATE_FORMAT = None
def setup_logging(level: str = "INFO") -> None:
web3_logger = logging.getLogger("web3.backend")
if web3_logger.handlers:
return
web3_logger.setLevel(getattr(logging, level.upper(), logging.INFO))
web3_logger.propagate = False
formatter = logging.Formatter(_LOG_FORMAT, _DATE_FORMAT)
fh = RotatingFileHandler(
APP_LOG_FILE,
maxBytes=10 * 1024 * 1024,
backupCount=5,
encoding="utf-8",
)
fh.setFormatter(formatter)
web3_logger.addHandler(fh)
ch = logging.StreamHandler()
ch.setFormatter(formatter)
web3_logger.addHandler(ch)
root = logging.getLogger()
if not root.handlers:
logging.basicConfig(level=level, format=_LOG_FORMAT, datefmt=_DATE_FORMAT)
def get_logger(name: str) -> logging.Logger:
if not name.startswith("web3."):
name = f"web3.{name}"
return logging.getLogger(name)
\ No newline at end of file
"""web3 后端 · FastAPI 入口(P1 骨架)"""
from __future__ import annotations
import os
import sys
from pathlib import Path
BACKEND_DIR = Path(__file__).resolve().parent
WEB3_DIR = BACKEND_DIR.parent
PROJECT_ROOT = WEB3_DIR.parent
sys.path.insert(0, str(PROJECT_ROOT))
# Windows GBK console 兼容
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from web3.backend._logging import setup_logging, get_logger
setup_logging(os.environ.get("LOG_LEVEL", "INFO"))
logger = get_logger("backend")
logger.info("=" * 60)
logger.info("web3 后端 · 进程启动")
logger.info(f"Python: {sys.version.split()[0]}, 平台: {sys.platform}")
logger.info(f"工作目录: {PROJECT_ROOT}")
# ── 创建 FastAPI 应用 ──
app = FastAPI(
title="数据治理 Web 工具 · web3 后端",
description="数据质量检测(任务 / 字段 / 规则 / 不合规)。前端在 web3/,由 Vite 5175 提供。",
version="0.1.0",
)
# CORS(开发模式:5175 是 Vite dev server)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── 健康检查 ──
@app.get("/api/health")
async def health():
return {"status": "ok", "service": "web3-backend"}
# ── 根路径(开发模式提示) ──
@app.get("/", response_class=HTMLResponse)
async def root():
return HTMLResponse(
"<h1>web3 后端 API 服务</h1>"
"<p>开发模式请用 <code>npm run dev</code> 启动 Vite(端口 5175)。</p>"
"<p>API 文档:<a href='/docs'>/docs</a></p>"
)
\ No newline at end of file
"""web3 后端路径与运行参数常量
- WEB3_DB_PATH:主业务 SQLite 文件(任务/字段/规则/不合规)
- WEB3_SOURCES_DIR:用户数据源 SQLite 所在目录(运行时只读扫描)
- WEB3_PORT / WEB3_HOST:FastAPI 监听端口(默认 8767,与 web2 8766 错开)
"""
from __future__ import annotations
import os
from pathlib import Path
# backend/ 的父目录 = web3/
WEB3_DIR = Path(__file__).resolve().parent
# web3 的父目录 = 项目根(含 data/)
PROJECT_ROOT = WEB3_DIR.parent
DATA_DIR = PROJECT_ROOT / "data"
DATA_DIR.mkdir(parents=True, exist_ok=True)
SOURCES_DIR = DATA_DIR / "sources"
SOURCES_DIR.mkdir(parents=True, exist_ok=True)
DB_PATH = Path(os.environ.get("WEB3_DB_PATH") or (DATA_DIR / "web3.db"))
PORT = int(os.environ.get("WEB3_PORT", "8767"))
HOST = os.environ.get("WEB3_HOST", "0.0.0.0")
LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
\ No newline at end of file
"""web3 后端 db 子包"""
from .database import engine, SessionLocal, init_db, get_session
__all__ = ["engine", "SessionLocal", "init_db", "get_session"]
\ No newline at end of file
"""SQLAlchemy 引擎与会话(P1 骨架)
- 单例 engine(sqlite + check_same_thread=False,FastAPI 跨线程访问需要)
- SessionLocal:每次请求拿一个新 session
- init_db() 占位;P2 起会读 schema.sql + seed
"""
from __future__ import annotations
from contextlib import contextmanager
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker, Session, declarative_base
from web3.backend.config import DB_PATH
# declarative_base 单例:所有模型 import 此 Base
Base = declarative_base()
# ── engine ──
engine = create_engine(
f"sqlite:///{DB_PATH}",
echo=False,
connect_args={"check_same_thread": False},
)
# SQLite 外键约束默认是关的,打开它
@event.listens_for(engine, "connect")
def _enable_sqlite_fk(dbapi_conn, _):
cur = dbapi_conn.cursor()
cur.execute("PRAGMA foreign_keys = ON")
cur.close()
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
# ── FastAPI Depends ──
def get_session() -> Session:
db = SessionLocal()
try:
yield db
finally:
db.close()
@contextmanager
def session_scope() -> Session:
db = SessionLocal()
try:
yield db
db.commit()
except Exception:
db.rollback()
raise
finally:
db.close()
# ── 首次启动初始化(占位,P2 接入 schema + seed) ──
def init_db() -> None:
"""P2 起:若 DB 文件不存在,建表 + 灌种子。已存在则什么都不做。"""
pass
\ No newline at end of file
"""web3 后端 ORM 模型子包
每个文件对应 schema.sql 里的一张表。
"""
from .task_group import TaskGroup
__all__ = ["TaskGroup"]
\ No newline at end of file
fastapi>=0.110
uvicorn[standard]>=0.27
sqlalchemy>=2.0
pydantic>=2.5
\ No newline at end of file
"""web3 后端 routers 子包"""
\ No newline at end of file
"""web3 后端启动脚本(开发模式)
用法:
cd 数据治理
python -m web3.backend.start
# 或
python web3/backend/start.py
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
BACKEND_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = BACKEND_DIR.parent
sys.path.insert(0, str(PROJECT_ROOT))
# Windows GBK console 兼容
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
import uvicorn
from web3.backend.config import HOST, PORT
from web3.backend._logging import setup_logging, get_logger, LOG_DIR, APP_LOG_FILE
setup_logging(os.environ.get("LOG_LEVEL", "INFO"))
logger = get_logger("backend.start")
logger.info("=" * 60)
logger.info("web3 后端 · 进程启动")
logger.info(f"Python: {sys.version.split()[0]}, 平台: {sys.platform}")
logger.info(f"工作目录: {PROJECT_ROOT}")
logger.info(f"应用日志: {APP_LOG_FILE}")
if __name__ == "__main__":
print("=" * 60)
print(f" web3 后端 · FastAPI")
print(f" API: http://localhost:{PORT}/api")
print(f" API 文档: http://localhost:{PORT}/docs")
print(f" 前端 dev: http://localhost:5175 (Vite 单独启动)")
print(f" 应用日志: {APP_LOG_FILE}")
print("=" * 60)
uvicorn.run(
"web3.backend.app:app",
host=HOST,
port=PORT,
reload=False,
log_level="info",
)
\ No newline at end of file
@echo off @echo off
REM ============================================================ REM web3 - Windows launcher (backend + frontend, same window)
REM web3 - Windows launcher (frontend only)
REM REM
REM 启动后: REM 启动后:
REM - 前端 Vite dev server http://localhost:5175 REM - 后端 FastAPI http://localhost:8767 /api/*
REM - 前端 Vite dev server http://localhost:5175
REM REM
REM 关闭此窗口或 Ctrl+C 即可停止 dev server。 REM 关闭此窗口或 Ctrl+C 即可停止 dev server.
REM ============================================================ REM (后端在同窗口后台跑,窗口关闭 = 全停)
REM Switch to project root (parent of web3/) REM Switch to project root (parent of web3/)
cd /d "%~dp0\.." cd /d "%~dp0\.."
echo. echo.
echo ============================================================ echo ============================================================
echo web3 - Data Governance Tool (frontend only) echo web3 - Data Governance Tool (backend + frontend)
echo ============================================================ echo ============================================================
echo. echo.
REM Check Node + npm REM -- 前置检查:Node + npm --
where npm >nul 2>&1 where npm >nul 2>&1
if errorlevel 1 ( if errorlevel 1 (
echo [ERROR] npm not found. Please install Node.js 18+ first. echo [ERROR] npm not found. Please install Node.js 18+ first.
...@@ -25,7 +25,27 @@ if errorlevel 1 ( ...@@ -25,7 +25,27 @@ if errorlevel 1 (
exit /b 1 exit /b 1
) )
REM Check if node_modules exists REM -- 前置检查:Python --
where python >nul 2>&1
if errorlevel 1 (
echo [ERROR] python not found. Please install Python 3.10+ first.
pause
exit /b 1
)
REM -- 前置检查:后端依赖(缺则装) --
python -c "import fastapi, sqlalchemy" 2>nul
if errorlevel 1 (
echo [INFO] Installing backend deps...
python -m pip install -r web3\backend\requirements.txt
if errorlevel 1 (
echo [ERROR] Failed to install backend deps.
pause
exit /b 1
)
)
REM -- 前置检查:前端 node_modules(缺则装) --
if not exist "web3\node_modules" ( if not exist "web3\node_modules" (
echo [INFO] Installing frontend deps... echo [INFO] Installing frontend deps...
cd web3 && npm install && cd .. cd web3 && npm install && cd ..
...@@ -36,6 +56,32 @@ if not exist "web3\node_modules" ( ...@@ -36,6 +56,32 @@ if not exist "web3\node_modules" (
) )
) )
echo Starting frontend... REM -- 确保 backend\logs 目录存在 --
if not exist "web3\backend\logs" mkdir "web3\backend\logs"
REM -- 启动后端(后台进程,cwd 保持项目根以保证 web3 包可被 import) --
echo Starting backend on :8767 ...
start /b "" python -m web3.backend.start > "web3\backend\logs\console.log" 2>&1
REM -- 等后端就绪(轮询 /api/health,最多 20s) --
echo Waiting for backend ...
set /a tries=0
:wait_backend
set /a tries+=1
if %tries% gtr 20 (
echo [ERROR] Backend failed to start in 20s. Check web3\backend\logs\console.log
pause
exit /b 1
)
curl -sS http://localhost:8767/api/health >nul 2>&1
if errorlevel 1 (
REM ping 比 timeout 更兼容:等约 1s
ping -n 2 127.0.0.1 >nul 2>&1
goto wait_backend
)
echo Backend ready (http://localhost:8767).
REM -- 启动前端(前台,Ctrl+C 停止) --
echo Starting frontend on :5175 ...
cd /d "%CD%\web3" cd /d "%CD%\web3"
npm run dev npm run dev
\ No newline at end of file
...@@ -12,5 +12,12 @@ export default defineConfig({ ...@@ -12,5 +12,12 @@ export default defineConfig({
server: { server: {
port: 5175, port: 5175,
open: false, open: false,
// /api/* 反向代理到 web3 后端(FastAPI @ 8767),避免浏览器跨域
proxy: {
'/api': {
target: 'http://localhost:8767',
changeOrigin: true,
},
},
}, },
}) })
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment