@@ -5,14 +5,50 @@ SQLite 数据库连接工具 (本地化适配版)
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import shutil
|
||||||
from typing import Optional, Any, List, Dict
|
from typing import Optional, Any, List, Dict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from app.utils.logger import get_logger
|
from app.utils.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
# 数据库文件路径
|
# 数据库文件默认路径(兼容旧行为:backend_api_python/quantdinger.db)
|
||||||
DB_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'quantdinger.db')
|
_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
_LEGACY_DB_FILE = os.path.join(_BASE_DIR, 'quantdinger.db')
|
||||||
|
|
||||||
|
|
||||||
|
def _get_db_file() -> str:
|
||||||
|
"""
|
||||||
|
Resolve SQLite DB file path.
|
||||||
|
|
||||||
|
Priority:
|
||||||
|
- SQLITE_DATABASE_FILE env (Docker 推荐:/app/data/quantdinger.db)
|
||||||
|
- legacy default: backend_api_python/quantdinger.db
|
||||||
|
|
||||||
|
Also performs a best-effort one-time migration:
|
||||||
|
If the configured path doesn't exist but legacy db exists, copy legacy to configured path.
|
||||||
|
"""
|
||||||
|
env_path = os.getenv('SQLITE_DATABASE_FILE')
|
||||||
|
db_path = (env_path or '').strip() or _LEGACY_DB_FILE
|
||||||
|
|
||||||
|
# Ensure parent dir exists
|
||||||
|
parent = os.path.dirname(db_path)
|
||||||
|
if parent and not os.path.exists(parent):
|
||||||
|
try:
|
||||||
|
os.makedirs(parent, exist_ok=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Best-effort migration from legacy path
|
||||||
|
try:
|
||||||
|
if os.path.abspath(db_path) != os.path.abspath(_LEGACY_DB_FILE):
|
||||||
|
if (not os.path.exists(db_path)) and os.path.exists(_LEGACY_DB_FILE):
|
||||||
|
shutil.copy2(_LEGACY_DB_FILE, db_path)
|
||||||
|
logger.info(f"Migrated SQLite DB from legacy path to {db_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"SQLite DB migration skipped/failed: {e}")
|
||||||
|
|
||||||
|
return db_path
|
||||||
|
|
||||||
# 线程锁,用于简单的并发控制(SQLite 对写操作有限制)
|
# 线程锁,用于简单的并发控制(SQLite 对写操作有限制)
|
||||||
_db_lock = threading.Lock()
|
_db_lock = threading.Lock()
|
||||||
@@ -465,14 +501,16 @@ def get_db_connection():
|
|||||||
# 初始化表结构
|
# 初始化表结构
|
||||||
if not _has_initialized:
|
if not _has_initialized:
|
||||||
try:
|
try:
|
||||||
conn_init = sqlite3.connect(DB_FILE)
|
db_file = _get_db_file()
|
||||||
|
conn_init = sqlite3.connect(db_file)
|
||||||
_init_db_schema(conn_init)
|
_init_db_schema(conn_init)
|
||||||
conn_init.close()
|
conn_init.close()
|
||||||
_has_initialized = True
|
_has_initialized = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to initialize database: {e}")
|
logger.error(f"Failed to initialize database: {e}")
|
||||||
|
|
||||||
conn = SQLiteConnection(DB_FILE)
|
db_file = _get_db_file()
|
||||||
|
conn = SQLiteConnection(db_file)
|
||||||
try:
|
try:
|
||||||
# with _db_lock: # SQLite 内部有锁,这里如果不跨线程共享连接其实不用强加锁
|
# with _db_lock: # SQLite 内部有锁,这里如果不跨线程共享连接其实不用强加锁
|
||||||
yield conn
|
yield conn
|
||||||
@@ -488,14 +526,16 @@ def get_db_connection_sync():
|
|||||||
global _has_initialized
|
global _has_initialized
|
||||||
if not _has_initialized:
|
if not _has_initialized:
|
||||||
try:
|
try:
|
||||||
conn_init = sqlite3.connect(DB_FILE)
|
db_file = _get_db_file()
|
||||||
|
conn_init = sqlite3.connect(db_file)
|
||||||
_init_db_schema(conn_init)
|
_init_db_schema(conn_init)
|
||||||
conn_init.close()
|
conn_init.close()
|
||||||
_has_initialized = True
|
_has_initialized = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to initialize database: {e}")
|
logger.error(f"Failed to initialize database: {e}")
|
||||||
|
|
||||||
return SQLiteConnection(DB_FILE)
|
db_file = _get_db_file()
|
||||||
|
return SQLiteConnection(db_file)
|
||||||
|
|
||||||
def close_db_connection():
|
def close_db_connection():
|
||||||
pass
|
pass
|
||||||
|
|||||||
+2
-1
@@ -19,7 +19,8 @@ services:
|
|||||||
- ./backend_api_python/logs:/app/logs
|
- ./backend_api_python/logs:/app/logs
|
||||||
- ./backend_api_python/data:/app/data
|
- ./backend_api_python/data:/app/data
|
||||||
# Configuration file (optional, for development)
|
# Configuration file (optional, for development)
|
||||||
- ./backend_api_python/.env:/app/.env:ro
|
# NOTE: must be writable for /api/settings/save to persist changes
|
||||||
|
- ./backend_api_python/.env:/app/.env
|
||||||
environment:
|
environment:
|
||||||
- PYTHON_API_HOST=0.0.0.0
|
- PYTHON_API_HOST=0.0.0.0
|
||||||
- PYTHON_API_PORT=5000
|
- PYTHON_API_PORT=5000
|
||||||
|
|||||||
Reference in New Issue
Block a user