238 lines
6.7 KiB
Python
238 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
数据库模块(稳定 API 门面)- SQLite
|
||
|
||
P2 / O-07:拆分 `database.py` 为稳定门面 + `db/` 分域实现。
|
||
|
||
约束:
|
||
- 外部仍通过 `import database` 调用(函数名/参数/返回结构保持不变)
|
||
- schema/migration 入口统一,避免 scattered ALTER TABLE
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import threading
|
||
import time
|
||
from typing import Optional
|
||
|
||
import db_pool
|
||
from app_config import get_config
|
||
|
||
from db.schema import ensure_schema
|
||
from db.migrations import migrate_database as _migrate_database
|
||
from db.admin import (
|
||
admin_reset_user_password,
|
||
clean_old_operation_logs,
|
||
ensure_default_admin,
|
||
get_hourly_registration_count,
|
||
get_system_config_raw as _get_system_config_raw,
|
||
get_system_stats,
|
||
update_admin_password,
|
||
update_admin_username,
|
||
update_system_config as _update_system_config,
|
||
verify_admin,
|
||
)
|
||
from db.accounts import (
|
||
create_account,
|
||
delete_account,
|
||
delete_user_accounts,
|
||
get_account,
|
||
get_account_status,
|
||
get_account_status_batch,
|
||
get_user_accounts,
|
||
increment_account_login_fail,
|
||
reset_account_login_status,
|
||
update_account_remark,
|
||
)
|
||
from db.announcements import (
|
||
create_announcement,
|
||
delete_announcement,
|
||
dismiss_announcement_for_user,
|
||
get_active_announcement_for_user,
|
||
get_announcement_by_id,
|
||
get_announcements,
|
||
set_announcement_active,
|
||
)
|
||
from db.email import (
|
||
get_user_by_email,
|
||
get_user_email_notify,
|
||
update_user_email,
|
||
update_user_email_notify,
|
||
)
|
||
from db.feedbacks import (
|
||
close_feedback,
|
||
create_bug_feedback,
|
||
delete_feedback,
|
||
get_bug_feedbacks,
|
||
get_feedback_by_id,
|
||
get_feedback_stats,
|
||
get_user_feedbacks,
|
||
reply_feedback,
|
||
)
|
||
from db.schedules import (
|
||
clean_old_schedule_logs,
|
||
create_schedule_execution_log,
|
||
create_user_schedule,
|
||
delete_schedule_logs,
|
||
delete_user_schedule,
|
||
get_due_user_schedules,
|
||
get_enabled_user_schedules,
|
||
get_schedule_by_id,
|
||
get_schedule_execution_logs,
|
||
get_user_all_schedule_logs,
|
||
get_user_schedules,
|
||
recompute_schedule_next_run,
|
||
toggle_user_schedule,
|
||
update_schedule_next_run,
|
||
update_schedule_execution_log,
|
||
update_schedule_last_run,
|
||
update_user_schedule,
|
||
)
|
||
from db.tasks import create_task_log, delete_old_task_logs, get_task_logs, get_task_stats, get_user_run_stats
|
||
from db.users import (
|
||
approve_user,
|
||
create_user,
|
||
delete_user,
|
||
extend_user_vip,
|
||
get_all_users,
|
||
get_pending_users,
|
||
get_user_by_id,
|
||
get_user_by_username,
|
||
get_user_kdocs_settings,
|
||
get_user_stats,
|
||
get_user_vip_info,
|
||
get_vip_config,
|
||
is_user_vip,
|
||
reject_user,
|
||
remove_user_vip,
|
||
set_default_vip_days,
|
||
set_user_vip,
|
||
update_user_kdocs_settings,
|
||
verify_user,
|
||
)
|
||
from db.security import record_login_context
|
||
|
||
config = get_config()
|
||
|
||
# 数据库文件路径
|
||
DB_FILE = config.DB_FILE
|
||
|
||
# 数据库版本 (用于迁移管理)
|
||
DB_VERSION = 17
|
||
|
||
|
||
# ==================== 系统配置缓存(P1 / O-03) ====================
|
||
|
||
_system_config_cache_lock = threading.Lock()
|
||
_system_config_cache_value: Optional[dict] = None
|
||
_system_config_cache_loaded_at = 0.0
|
||
try:
|
||
_SYSTEM_CONFIG_CACHE_TTL_SECONDS = float(os.environ.get("SYSTEM_CONFIG_CACHE_TTL_SECONDS", "3"))
|
||
except Exception:
|
||
_SYSTEM_CONFIG_CACHE_TTL_SECONDS = 3.0
|
||
_SYSTEM_CONFIG_CACHE_TTL_SECONDS = max(0.0, _SYSTEM_CONFIG_CACHE_TTL_SECONDS)
|
||
|
||
|
||
def invalidate_system_config_cache() -> None:
|
||
global _system_config_cache_value, _system_config_cache_loaded_at
|
||
with _system_config_cache_lock:
|
||
_system_config_cache_value = None
|
||
_system_config_cache_loaded_at = 0.0
|
||
|
||
|
||
def init_database():
|
||
"""初始化数据库表结构 + 迁移(入口统一)。"""
|
||
db_pool.init_pool(DB_FILE, pool_size=config.DB_POOL_SIZE)
|
||
|
||
with db_pool.get_db() as conn:
|
||
ensure_schema(conn)
|
||
_migrate_database(conn, DB_VERSION)
|
||
|
||
ensure_default_admin()
|
||
|
||
|
||
def migrate_database():
|
||
"""数据库迁移(对外保留接口)。"""
|
||
with db_pool.get_db() as conn:
|
||
ensure_schema(conn)
|
||
_migrate_database(conn, DB_VERSION)
|
||
|
||
|
||
# ==================== 系统配置管理(缓存门面) ====================
|
||
|
||
|
||
def get_system_config():
|
||
"""获取系统配置(带进程内缓存)。"""
|
||
global _system_config_cache_value, _system_config_cache_loaded_at
|
||
|
||
now_ts = time.time()
|
||
with _system_config_cache_lock:
|
||
if _system_config_cache_value is not None:
|
||
if _SYSTEM_CONFIG_CACHE_TTL_SECONDS <= 0 or (now_ts - _system_config_cache_loaded_at) < _SYSTEM_CONFIG_CACHE_TTL_SECONDS:
|
||
return dict(_system_config_cache_value)
|
||
|
||
value = _get_system_config_raw()
|
||
|
||
with _system_config_cache_lock:
|
||
_system_config_cache_value = dict(value)
|
||
_system_config_cache_loaded_at = now_ts
|
||
return dict(value)
|
||
|
||
|
||
def update_system_config(
|
||
max_concurrent=None,
|
||
schedule_enabled=None,
|
||
schedule_time=None,
|
||
schedule_browse_type=None,
|
||
schedule_weekdays=None,
|
||
max_concurrent_per_account=None,
|
||
max_screenshot_concurrent=None,
|
||
enable_screenshot=None,
|
||
proxy_enabled=None,
|
||
proxy_api_url=None,
|
||
proxy_expire_minutes=None,
|
||
auto_approve_enabled=None,
|
||
auto_approve_hourly_limit=None,
|
||
auto_approve_vip_days=None,
|
||
kdocs_enabled=None,
|
||
kdocs_doc_url=None,
|
||
kdocs_default_unit=None,
|
||
kdocs_sheet_name=None,
|
||
kdocs_sheet_index=None,
|
||
kdocs_unit_column=None,
|
||
kdocs_image_column=None,
|
||
kdocs_admin_notify_enabled=None,
|
||
kdocs_admin_notify_email=None,
|
||
):
|
||
"""更新系统配置(写入后立即失效缓存)。"""
|
||
ok = _update_system_config(
|
||
max_concurrent=max_concurrent,
|
||
schedule_enabled=schedule_enabled,
|
||
schedule_time=schedule_time,
|
||
schedule_browse_type=schedule_browse_type,
|
||
schedule_weekdays=schedule_weekdays,
|
||
max_concurrent_per_account=max_concurrent_per_account,
|
||
max_screenshot_concurrent=max_screenshot_concurrent,
|
||
enable_screenshot=enable_screenshot,
|
||
proxy_enabled=proxy_enabled,
|
||
proxy_api_url=proxy_api_url,
|
||
proxy_expire_minutes=proxy_expire_minutes,
|
||
auto_approve_enabled=auto_approve_enabled,
|
||
auto_approve_hourly_limit=auto_approve_hourly_limit,
|
||
auto_approve_vip_days=auto_approve_vip_days,
|
||
kdocs_enabled=kdocs_enabled,
|
||
kdocs_doc_url=kdocs_doc_url,
|
||
kdocs_default_unit=kdocs_default_unit,
|
||
kdocs_sheet_name=kdocs_sheet_name,
|
||
kdocs_sheet_index=kdocs_sheet_index,
|
||
kdocs_unit_column=kdocs_unit_column,
|
||
kdocs_image_column=kdocs_image_column,
|
||
kdocs_admin_notify_enabled=kdocs_admin_notify_enabled,
|
||
kdocs_admin_notify_email=kdocs_admin_notify_email,
|
||
)
|
||
if ok:
|
||
invalidate_system_config_cache()
|
||
return ok
|