1414
1515from ._deepseek_config import DEEPSEEK_API_URL , DEEPSEEK_KEY , DEEPSEEK_MODEL
1616from ._http import shared_async_client
17+ from .runtime_store import QuotaReservation , RuntimeStore
1718
1819# ═══════════════════════════════════════════════════════════════
1920# 配置
2021# ═══════════════════════════════════════════════════════════════
2122
2223_ACTIVATION_FILE = Path (__file__ ).resolve ().parent / "activation_codes.json"
24+ _RUNTIME_DB = Path (
25+ os .getenv ("BAZI_RUNTIME_DB_PATH" , Path (__file__ ).resolve ().parents [2 ] / "data" / "runtime.sqlite3" )
26+ )
2327_RUNTIME_DATA_LOCK = threading .RLock ()
2428
2529# ═══════════════════════════════════════════════════════════════
@@ -383,8 +387,33 @@ async def call_deepseek_stream(messages: list[dict]) -> AsyncGenerator[str]:
383387_default_codes = dict (_DEMO_CODES ) if _USE_DEMO_CODES else {}
384388
385389
390+ def _use_sqlite_runtime_store () -> bool :
391+ return os .getenv ("BAZI_RUNTIME_STORE" , "json" ).lower () == "sqlite"
392+
393+
394+ def _activation_seed_codes () -> dict :
395+ codes = dict (_default_codes )
396+ env_codes = os .getenv ("ACTIVATION_CODES" , "" )
397+ if env_codes :
398+ with suppress (json .JSONDecodeError ):
399+ codes .update (json .loads (env_codes ))
400+ if os .getenv ("BAZI_PUBLIC" , "" ).lower () in ("1" , "true" , "yes" ):
401+ for code in _DEMO_CODES :
402+ codes .pop (code , None )
403+ return codes
404+
405+
406+ def _runtime_store () -> RuntimeStore :
407+ store = RuntimeStore (_RUNTIME_DB )
408+ store .seed_activation_codes (_activation_seed_codes ())
409+ return store
410+
411+
386412def _load_codes () -> dict :
387413 """加载激活码(环境变量 + 本地文件合并,环境变量优先)"""
414+ if _use_sqlite_runtime_store ():
415+ return _runtime_store ().activation_codes ()
416+
388417 codes = dict (_default_codes )
389418
390419 # 本地文件(首次自动创建,gitignore 保护)
@@ -442,6 +471,14 @@ def _atomic_json_write(path: Path, data: dict, **json_kwargs) -> None:
442471
443472def validate_code (code : str ) -> tuple [bool , int , str ]:
444473 """验证激活码。返回 (有效?, 剩余次数, 消息)"""
474+ if _use_sqlite_runtime_store ():
475+ remaining = _runtime_store ().activation_remaining (code )
476+ if remaining is None :
477+ return False , 0 , "激活码无效"
478+ if remaining <= 0 :
479+ return False , 0 , "该激活码次数已用完"
480+ return True , remaining , "有效"
481+
445482 codes = _load_codes ()
446483 entry = codes .get (code .strip ().upper ())
447484 if not entry :
@@ -454,6 +491,13 @@ def validate_code(code: str) -> tuple[bool, int, str]:
454491
455492def consume_code (code : str ) -> tuple [bool , int ]:
456493 """消耗一次激活码。返回 (成功?, 剩余次数)"""
494+ if _use_sqlite_runtime_store ():
495+ reservation = reserve_activation_code (code )
496+ if reservation is None :
497+ return False , 0
498+ _runtime_store ().settle_reservation (reservation .reservation_id )
499+ return True , reservation .remaining
500+
457501 with _RUNTIME_DATA_LOCK :
458502 codes = _load_codes ()
459503 entry = codes .get (code .strip ().upper ())
@@ -498,6 +542,10 @@ def check_free_quota(client_id: str) -> tuple[bool, int]:
498542 IP 经哈希处理后存储,不保留原始 IP。
499543 注意: NAT/代理环境下多用户共用同一 IP,一个用户用完额度其他人也会被拦。
500544 """
545+ if _use_sqlite_runtime_store ():
546+ remaining = _runtime_store ().free_remaining (_hash_ip (client_id ), time .strftime ("%Y-%m-%d" ), FREE_DAILY_LIMIT )
547+ return remaining > 0 , remaining
548+
501549 with _RUNTIME_DATA_LOCK :
502550 today = time .strftime ("%Y-%m-%d" )
503551 data = _load_free_usage ()
@@ -521,6 +569,13 @@ def check_free_quota(client_id: str) -> tuple[bool, int]:
521569
522570def consume_free_quota (client_id : str ) -> int :
523571 """消耗一次免费额度。返回剩余次数"""
572+ if _use_sqlite_runtime_store ():
573+ reservation = reserve_free_quota (client_id )
574+ if reservation is None :
575+ return 0
576+ _runtime_store ().settle_reservation (reservation .reservation_id )
577+ return reservation .remaining
578+
524579 with _RUNTIME_DATA_LOCK :
525580 today = time .strftime ("%Y-%m-%d" )
526581 data = _load_free_usage ()
@@ -536,3 +591,27 @@ def consume_free_quota(client_id: str) -> int:
536591 data [key ] = entry
537592 _save_free_usage (data )
538593 return FREE_DAILY_LIMIT - entry ["count" ]
594+
595+
596+ def reserve_activation_code (code : str ) -> QuotaReservation | None :
597+ """Atomically reserve a paid quota until the first response token is delivered."""
598+ if not _use_sqlite_runtime_store ():
599+ return None
600+ return _runtime_store ().reserve_activation (code )
601+
602+
603+ def reserve_free_quota (client_id : str ) -> QuotaReservation | None :
604+ """Atomically reserve one daily free quota until the first response token is delivered."""
605+ if not _use_sqlite_runtime_store ():
606+ return None
607+ return _runtime_store ().reserve_free (
608+ _hash_ip (client_id ), time .strftime ("%Y-%m-%d" ), FREE_DAILY_LIMIT ,
609+ )
610+
611+
612+ def settle_quota_reservation (reservation_id : str ) -> bool :
613+ return _runtime_store ().settle_reservation (reservation_id )
614+
615+
616+ def release_quota_reservation (reservation_id : str ) -> bool :
617+ return _runtime_store ().release_reservation (reservation_id )
0 commit comments