fix db bug
This commit is contained in:
parent
6d636e9799
commit
79368c11e2
@ -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 认证
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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():
|
||||
|
||||
77
backend/migrate_sqlite_to_mysql.py
Normal file
77
backend/migrate_sqlite_to_mysql.py
Normal 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()
|
||||
@ -10,3 +10,4 @@ openai
|
||||
httpx
|
||||
apscheduler
|
||||
python-dotenv
|
||||
pymysql
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user