feat: add configuration management with async settings refresh and middleware integration
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
from apps.base.models import KeyValue
|
||||||
|
from apps.base.utils import ip_limit
|
||||||
|
from core.settings import DEFAULT_CONFIG, settings
|
||||||
|
|
||||||
|
|
||||||
|
async def ensure_settings_row() -> None:
|
||||||
|
await KeyValue.get_or_create(key="settings", defaults={"value": DEFAULT_CONFIG})
|
||||||
|
|
||||||
|
|
||||||
|
def _sync_ip_limits() -> None:
|
||||||
|
ip_limit["error"].minutes = settings.errorMinute
|
||||||
|
ip_limit["error"].count = settings.errorCount
|
||||||
|
ip_limit["upload"].minutes = settings.uploadMinute
|
||||||
|
ip_limit["upload"].count = settings.uploadCount
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh_settings() -> None:
|
||||||
|
"""从数据库读取最新配置并应用到运行时。"""
|
||||||
|
config_record = await KeyValue.filter(key="settings").first()
|
||||||
|
settings.user_config = config_record.value if config_record and config_record.value else {}
|
||||||
|
_sync_ip_limits()
|
||||||
+6
-3
@@ -11,15 +11,17 @@ from tortoise.expressions import Q
|
|||||||
|
|
||||||
from apps.base.models import FileCodes, UploadChunk
|
from apps.base.models import FileCodes, UploadChunk
|
||||||
from apps.base.utils import ip_limit, get_chunk_file_path_name
|
from apps.base.utils import ip_limit, get_chunk_file_path_name
|
||||||
|
from core.config import refresh_settings
|
||||||
from core.settings import settings, data_root
|
from core.settings import settings, data_root
|
||||||
from core.storage import FileStorageInterface, storages
|
from core.storage import FileStorageInterface, storages
|
||||||
from core.utils import get_now
|
from core.utils import get_now
|
||||||
|
|
||||||
|
|
||||||
async def delete_expire_files():
|
async def delete_expire_files():
|
||||||
file_storage: FileStorageInterface = storages[settings.file_storage]()
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
await refresh_settings()
|
||||||
|
file_storage: FileStorageInterface = storages[settings.file_storage]()
|
||||||
# 遍历 share目录下的所有文件夹,删除空的文件夹,并判断父目录是否为空,如果为空也删除
|
# 遍历 share目录下的所有文件夹,删除空的文件夹,并判断父目录是否为空,如果为空也删除
|
||||||
if settings.file_storage == "local":
|
if settings.file_storage == "local":
|
||||||
for root, dirs, files in os.walk(f"{data_root}/share/data"):
|
for root, dirs, files in os.walk(f"{data_root}/share/data"):
|
||||||
@@ -46,10 +48,11 @@ async def delete_expire_files():
|
|||||||
|
|
||||||
|
|
||||||
async def clean_incomplete_uploads():
|
async def clean_incomplete_uploads():
|
||||||
file_storage: FileStorageInterface = storages[settings.file_storage]()
|
|
||||||
expire_hours = getattr(settings, "chunk_expire_hours", 24)
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
await refresh_settings()
|
||||||
|
file_storage: FileStorageInterface = storages[settings.file_storage]()
|
||||||
|
expire_hours = getattr(settings, "chunk_expire_hours", 24)
|
||||||
now = await get_now()
|
now = await get_now()
|
||||||
expire_time = now - datetime.timedelta(hours=expire_hours)
|
expire_time = now - datetime.timedelta(hours=expire_hours)
|
||||||
expired_sessions = await UploadChunk.filter(
|
expired_sessions = await UploadChunk.filter(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from apps.admin.views import admin_api
|
|||||||
from apps.base.models import KeyValue
|
from apps.base.models import KeyValue
|
||||||
from apps.base.utils import ip_limit
|
from apps.base.utils import ip_limit
|
||||||
from apps.base.views import share_api, chunk_api, presign_api
|
from apps.base.views import share_api, chunk_api, presign_api
|
||||||
|
from core.config import ensure_settings_row, refresh_settings
|
||||||
from core.database import db_startup_lock, get_db_config, init_db
|
from core.database import db_startup_lock, get_db_config, init_db
|
||||||
from core.logger import logger
|
from core.logger import logger
|
||||||
from core.response import APIResponse
|
from core.response import APIResponse
|
||||||
@@ -58,13 +59,11 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
|
|
||||||
async def load_config():
|
async def load_config():
|
||||||
user_config, _ = await KeyValue.get_or_create(
|
await ensure_settings_row()
|
||||||
key="settings", defaults={"value": DEFAULT_CONFIG}
|
|
||||||
)
|
|
||||||
await KeyValue.update_or_create(
|
await KeyValue.update_or_create(
|
||||||
key="sys_start", defaults={"value": int(time.time() * 1000)}
|
key="sys_start", defaults={"value": int(time.time() * 1000)}
|
||||||
)
|
)
|
||||||
settings.user_config = user_config.value
|
await refresh_settings()
|
||||||
|
|
||||||
await migrate_password_to_hash()
|
await migrate_password_to_hash()
|
||||||
|
|
||||||
@@ -87,6 +86,11 @@ async def migrate_password_to_hash():
|
|||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def refresh_settings_middleware(request, call_next):
|
||||||
|
await refresh_settings()
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["*"],
|
||||||
|
|||||||
Reference in New Issue
Block a user