1+ from asyncio import Lock
2+ from copy import deepcopy
3+
14from sqlalchemy .ext .asyncio import AsyncSession
5+ from aiocache import cached
26
37from app import on_startup
48from app .core .manager import core_manager
59from app .db import GetDB
6- from app .db .crud .host import get_host_by_id , get_hosts , get_or_create_inbound
10+ from app .db .crud .host import get_host_by_id , get_hosts , upsert_inbounds
711from app .db .models import ProxyHost , ProxyHostSecurity
8- from app .models .host import MuxSettings , TransportSettings
9- from app .utils .store import DictStorage
12+ from app .models .host import MuxSettings , TransportSettings , BaseHost
1013
1114
1215def _prepare_host_data (host : ProxyHost ) -> dict :
@@ -40,18 +43,23 @@ def _prepare_host_data(host: ProxyHost) -> dict:
4043 }
4144
4245
43- @DictStorage
44- async def hosts (storage : dict , db : AsyncSession ):
45- inbounds_list = await core_manager .get_inbounds ()
46- for tag in inbounds_list :
47- await get_or_create_inbound (db , tag )
46+ class HostManager :
47+ def __init__ (self ):
48+ self ._hosts = {}
49+ self ._lock = Lock ()
50+
51+ async def setup (self , db : AsyncSession ):
52+ db_hosts = await get_hosts (db )
53+ await self .add_hosts (db , db_hosts )
4854
49- storage . clear ()
50- db_hosts = await get_hosts ( db )
55+ async def _reset_cache ( self ):
56+ await self . get_hosts . cache . clear ( )
5157
52- for host in db_hosts :
58+ @staticmethod
59+ async def _prepare_host_entry (db : AsyncSession , host : BaseHost , inbounds_list : list [str ]) -> tuple [int , dict ] | None :
5360 if host .is_disabled or (host .inbound_tag not in inbounds_list ):
54- continue
61+ return None
62+
5563 downstream = None
5664 if (
5765 host .transport_settings
@@ -67,10 +75,47 @@ async def hosts(storage: dict, db: AsyncSession):
6775 else :
6876 host_data ["downloadSettings" ] = None
6977
70- storage [host .id ] = host_data
78+ return (host .id , host_data )
79+
80+ async def add_host (self , db : AsyncSession , host : BaseHost ):
81+ await self .add_hosts (db , [host ])
82+
83+ async def add_hosts (self , db : AsyncSession , hosts : list [BaseHost ]):
84+ serialized_hosts = [BaseHost .model_validate (host ) for host in hosts ]
85+ inbounds_list = await core_manager .get_inbounds ()
86+ await upsert_inbounds (db , inbounds_list )
87+ await db .commit ()
88+
89+ prepared_hosts = []
90+ for host in serialized_hosts :
91+ result = await self ._prepare_host_entry (db , host , inbounds_list )
92+ if result :
93+ prepared_hosts .append (result )
94+
95+ # Acquire lock only for updating the dict and cache
96+ async with self ._lock :
97+ for host_id , host_data in prepared_hosts :
98+ self ._hosts [host_id ] = host_data
99+ await self ._reset_cache ()
100+
101+ async def remove_host (self , id : int ):
102+ async with self ._lock :
103+ self ._hosts .pop (id , None )
104+ await self ._reset_cache ()
105+
106+ async def get_host (self , id : int ) -> dict | None :
107+ async with self ._lock :
108+ return deepcopy (self ._hosts .get (id ))
109+
110+ @cached ()
111+ async def get_hosts (self ) -> dict [int , dict ]:
112+ async with self ._lock :
113+ return deepcopy (self ._hosts )
114+
71115
116+ host_manager : HostManager = HostManager ()
72117
73118@on_startup
74119async def initialize_hosts ():
75120 async with GetDB () as db :
76- await hosts . update (db )
121+ await host_manager . setup (db )
0 commit comments