fix db bug
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 1m25s
Build and Deploy Web / build-and-deploy (push) Successful in 56s

This commit is contained in:
zyc 2026-02-14 10:01:20 +08:00
parent 6d636e9799
commit 79368c11e2
6 changed files with 124 additions and 26 deletions

View File

@ -4,7 +4,15 @@ from dotenv import load_dotenv
load_dotenv()
# 数据库
# 数据库:优先从 DB_HOST 等环境变量拼接 MySQL URL否则回退到 DATABASE_URL / SQLite
DB_HOST = os.getenv("DB_HOST", "")
if DB_HOST:
DB_USER = os.getenv("DB_USER", "airlabs_manage")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "airlabs_manage")
DATABASE_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
else:
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./airlabs.db")
# JWT 认证

View File

@ -3,7 +3,9 @@ from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from config import DATABASE_URL
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
# check_same_thread 仅 SQLite 需要
connect_args = {"check_same_thread": False} if DATABASE_URL.startswith("sqlite") else {}
engine = create_engine(DATABASE_URL, connect_args=connect_args)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

View File

@ -21,6 +21,17 @@ logger = logging.getLogger(__name__)
# 创建所有表
Base.metadata.create_all(bind=engine)
# ── 手动迁移:为旧 SQLite 数据库补充 role_id 列 ──
from config import DATABASE_URL as _db_url
if _db_url.startswith("sqlite"):
import sqlalchemy
with engine.connect() as conn:
cols = [r[1] for r in conn.execute(sqlalchemy.text("PRAGMA table_info('users')"))]
if "role_id" not in cols:
conn.execute(sqlalchemy.text("ALTER TABLE users ADD COLUMN role_id INTEGER"))
conn.commit()
logger.info("[MIGRATE] added column users.role_id")
app = FastAPI(title="AirLabs Project", version="1.0.0")
# CORS
@ -144,6 +155,16 @@ def init_roles_and_admin():
print(f"[MIGRATE] added default milestones for project: {proj.name}")
db.commit()
# 迁移:为旧用户补充默认 role_id成员角色
member_role = db.query(Role).filter(Role.name == "成员").first()
if member_role:
orphans = db.query(User).filter(User.role_id.is_(None)).all()
for u in orphans:
u.role_id = member_role.id
print(f"[MIGRATE] assigned default role '成员' to user: {u.username}")
if orphans:
db.commit()
# 创建默认管理员(关联超级管理员角色)
admin_role = db.query(Role).filter(Role.name == "超级管理员").first()
if admin_role and not db.query(User).filter(User.username == "admin").first():

View File

@ -0,0 +1,77 @@
"""一次性脚本:将 SQLite 数据迁移到 MySQL"""
import sqlite3
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from config import DATABASE_URL
SQLITE_PATH = "airlabs.db"
# 按外键依赖顺序排列
TABLES = [
"roles",
"users",
"projects",
"project_milestones",
"submissions",
"submission_history",
"ai_tool_costs",
"ai_tool_cost_allocations",
"outsource_costs",
"cost_overrides",
"overhead_costs",
]
def migrate():
if not DATABASE_URL.startswith("mysql"):
print("ERROR: DATABASE_URL 不是 MySQL请检查 .env 配置")
return
# 连接 SQLite
sqlite_conn = sqlite3.connect(SQLITE_PATH)
sqlite_conn.row_factory = sqlite3.Row
# 连接 MySQL — 先建表
from database import Base, engine
import models # noqa: F401 — 确保所有模型已注册
Base.metadata.create_all(bind=engine)
print("[OK] MySQL tables created")
mysql_engine = engine
Session = sessionmaker(bind=mysql_engine)
session = Session()
try:
for table in TABLES:
rows = sqlite_conn.execute(f"SELECT * FROM {table}").fetchall()
if not rows:
print(f" {table}: 0 rows (skip)")
continue
cols = rows[0].keys()
col_list = ", ".join(cols)
param_list = ", ".join(f":{c}" for c in cols)
insert_sql = text(f"INSERT INTO {table} ({col_list}) VALUES ({param_list})")
# 清空目标表(避免重复运行冲突)
session.execute(text(f"DELETE FROM {table}"))
for row in rows:
data = dict(row)
session.execute(insert_sql, data)
session.commit()
print(f" {table}: {len(rows)} rows migrated")
print("\n[DONE] Migration complete!")
except Exception as e:
session.rollback()
print(f"\n[ERROR] Migration failed: {e}")
raise
finally:
session.close()
sqlite_conn.close()
if __name__ == "__main__":
migrate()

View File

@ -10,3 +10,4 @@ openai
httpx
apscheduler
python-dotenv
pymysql

View File

@ -1,15 +1,3 @@
# SQLite 持久化存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: airlabs-manage-sqlite-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
@ -33,18 +21,23 @@ spec:
ports:
- containerPort: 8000
env:
# SQLite 存储在持久卷中
- name: DATABASE_URL
value: "sqlite:////app/data/airlabs.db"
# 生产环境 JWT 密钥(部署前请修改)
# Database (阿里云 RDS MySQL)
- name: DB_HOST
value: "rm-7xv1uaw910558p1788o.mysql.rds.aliyuncs.com"
- name: DB_NAME
value: "airlabs-manage"
- name: DB_USER
value: "airlabs_manage"
- name: DB_PASSWORD
value: "Airlabs-manage123"
- name: DB_PORT
value: "3306"
# 生产环境 JWT 密钥
- name: SECRET_KEY
value: "Ui5-xEvtAhKRDtlXKzDfd7TElsVZFUhakff0qcjn8jU"
# CORS 允许的域名
- name: CORS_ORIGINS
value: "https://airlabs-manage-web.airlabs.art"
volumeMounts:
- name: sqlite-data
mountPath: /app/data
resources:
requests:
memory: "128Mi"
@ -52,10 +45,6 @@ spec:
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: sqlite-data
persistentVolumeClaim:
claimName: airlabs-manage-sqlite-data
---
apiVersion: v1
kind: Service