conf.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # coding=utf-8
  2. """
  3. @project: MaxKB
  4. @Author:虎虎
  5. @file: conf.py
  6. @date:2025/4/11 16:58
  7. @desc:
  8. """
  9. import errno
  10. import logging
  11. import os
  12. import yaml
  13. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  14. PROJECT_DIR = os.path.dirname(BASE_DIR)
  15. logger = logging.getLogger('maxkb.conf')
  16. class Config(dict):
  17. defaults = {
  18. # 数据库相关配置
  19. "DB_HOST": "127.0.0.1",
  20. "DB_PORT": 5432,
  21. "DB_USER": "postgres",
  22. "DB_PASSWORD": "",
  23. "DB_ENGINE": "dj_db_conn_pool.backends.postgresql",
  24. "DB_MAX_OVERFLOW": 80,
  25. 'LOCAL_MODEL_HOST': '127.0.0.1',
  26. 'LOCAL_MODEL_PORT': '11636',
  27. 'LOCAL_MODEL_PROTOCOL': "http",
  28. 'LOCAL_MODEL_HOST_WORKER': 1,
  29. # 语言
  30. 'LANGUAGE_CODE': 'zh-CN',
  31. "DEBUG": False,
  32. # redis host
  33. 'REDIS_HOST': '127.0.0.1',
  34. # 端口
  35. 'REDIS_PORT': 6379,
  36. # 密码
  37. 'REDIS_PASSWORD': '',
  38. # 库
  39. 'REDIS_DB': 0,
  40. # 最大连接数
  41. 'REDIS_MAX_CONNECTIONS': 100
  42. }
  43. def get_debug(self) -> bool:
  44. return self.get('DEBUG') if 'DEBUG' in self else True
  45. def get_time_zone(self) -> str:
  46. return self.get('TIME_ZONE') if 'TIME_ZONE' in self else 'Asia/Shanghai'
  47. def get_db_setting(self) -> dict:
  48. return {
  49. "NAME": self.get('DB_NAME'),
  50. "HOST": self.get('DB_HOST'),
  51. "PORT": self.get('DB_PORT'),
  52. "USER": self.get('DB_USER'),
  53. "PASSWORD": self.get('DB_PASSWORD'),
  54. "ENGINE": self.get('DB_ENGINE'),
  55. "CONN_MAX_AGE": 0,
  56. "POOL_OPTIONS": {
  57. "POOL_SIZE": 20,
  58. "MAX_OVERFLOW": int(self.get('DB_MAX_OVERFLOW')),
  59. "RECYCLE": 1800,
  60. "PRE_PING": True,
  61. "TIMEOUT": 30
  62. }
  63. }
  64. def get_cache_setting(self):
  65. redis_config = {
  66. 'default': {
  67. 'BACKEND': 'django_redis.cache.RedisCache',
  68. 'LOCATION': f'redis://{self.get("REDIS_HOST")}:{self.get("REDIS_PORT")}/{self.get("REDIS_DB")}',
  69. 'OPTIONS': {
  70. 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
  71. "PASSWORD": self.get("REDIS_PASSWORD"),
  72. "CONNECTION_POOL_KWARGS": {"max_connections": int(self.get("REDIS_MAX_CONNECTIONS"))}
  73. },
  74. },
  75. }
  76. if self.get('REDIS_SENTINEL_SENTINELS') is not None:
  77. sentinels_str = self.get('REDIS_SENTINEL_SENTINELS')
  78. sentinels = [
  79. (host.strip(), int(port))
  80. for hostport in sentinels_str.split(',')
  81. for host, port in [hostport.strip().split(':')]
  82. ]
  83. redis_config['default']['LOCATION'] = f'redis://{self.get("REDIS_SENTINEL_MASTER")}/{self.get("REDIS_DB")}'
  84. redis_config['default']['OPTIONS'].update({
  85. 'CLIENT_CLASS': 'django_redis.client.SentinelClient',
  86. 'SENTINELS': sentinels,
  87. 'SENTINEL_MASTER': self.get('REDIS_SENTINEL_MASTER'),
  88. 'PASSWORD': self.get("REDIS_PASSWORD"),
  89. })
  90. return redis_config
  91. def get_language_code(self):
  92. return self.get('LANGUAGE_CODE', 'zh-CN')
  93. def get_log_level(self):
  94. return self.get('LOG_LEVEL', 'DEBUG')
  95. def get_sandbox_python_package_paths(self):
  96. return self.get('SANDBOX_PYTHON_PACKAGE_PATHS',
  97. '/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages')
  98. def get_admin_path(self):
  99. return self.get('ADMIN_PATH', '/admin')
  100. def get_chat_path(self):
  101. return self.get('CHAT_PATH', '/chat')
  102. def get_builder_path(self):
  103. return self.get('BUILDER_PATH', '/builder')
  104. def get_session_timeout(self):
  105. return int(self.get('SESSION_TIMEOUT', 28800))
  106. def __init__(self, *args):
  107. super().__init__(*args)
  108. def __repr__(self):
  109. return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
  110. def __getitem__(self, item):
  111. return self.get(item)
  112. def __getattr__(self, item):
  113. return self.get(item)
  114. class ConfigManager:
  115. config_class = Config
  116. def __init__(self, root_path=None):
  117. self.root_path = root_path
  118. self.config = self.config_class()
  119. for key in self.config_class.defaults:
  120. self.config[key] = self.config_class.defaults[key]
  121. def from_mapping(self, *mapping, **kwargs):
  122. """Updates the config like :meth:`update` ignoring items with non-upper
  123. keys.
  124. .. versionadded:: 0.11
  125. """
  126. mappings = []
  127. if len(mapping) == 1:
  128. if hasattr(mapping[0], 'items'):
  129. mappings.append(mapping[0].items())
  130. else:
  131. mappings.append(mapping[0])
  132. elif len(mapping) > 1:
  133. raise TypeError(
  134. 'expected at most 1 positional argument, got %d' % len(mapping)
  135. )
  136. mappings.append(kwargs.items())
  137. for mapping in mappings:
  138. for (key, value) in mapping:
  139. if key.isupper():
  140. self.config[key] = value
  141. return True
  142. def from_yaml(self, filename, silent=False):
  143. if self.root_path:
  144. filename = os.path.join(self.root_path, filename)
  145. try:
  146. with open(filename, 'rt', encoding='utf8') as f:
  147. obj = yaml.safe_load(f)
  148. except IOError as e:
  149. if silent and e.errno in (errno.ENOENT, errno.EISDIR):
  150. return False
  151. e.strerror = 'Unable to load configuration file (%s)' % e.strerror
  152. raise
  153. if obj:
  154. return self.from_mapping(obj)
  155. return True
  156. def load_from_yml(self):
  157. for i in ['config_example.yml', 'config.yaml', 'config.yml']:
  158. if not os.path.isfile(os.path.join(self.root_path, i)):
  159. continue
  160. loaded = self.from_yaml(i)
  161. if loaded:
  162. return True
  163. msg = f"""
  164. Error: No config file found.
  165. You can run `cp config_example.yml {self.root_path}/config.yml`, and edit it.
  166. """
  167. raise ImportError(msg)
  168. def load_from_env(self):
  169. keys = os.environ.keys()
  170. config = {key.replace('MAXKB_', ''): os.environ.get(key) for key in keys if key.startswith('MAXKB_')}
  171. if len(config.keys()) <= 0:
  172. msg = f"""
  173. Error: No config env found.
  174. Please set environment variables
  175. MAXKB_CONFIG_TYPE: 配置文件读取方式 FILE: 使用配置文件配置 ENV: 使用ENV配置
  176. MAXKB_DB_NAME: 数据库名称
  177. MAXKB_DB_HOST: 数据库主机
  178. MAXKB_DB_PORT: 数据库端口
  179. MAXKB_DB_USER: 数据库用户名
  180. MAXKB_DB_PASSWORD: 数据库密码
  181. MAXKB_REDIS_HOST:缓存数据库主机
  182. MAXKB_REDIS_PORT:缓存数据库端口
  183. MAXKB_REDIS_PASSWORD:缓存数据库密码
  184. MAXKB_REDIS_DB:缓存数据库
  185. MAXKB_REDIS_MAX_CONNECTIONS:缓存数据库最大连接数
  186. """
  187. raise ImportError(msg)
  188. self.from_mapping(config)
  189. return True
  190. @classmethod
  191. def load_user_config(cls, root_path=None, config_class=None):
  192. config_class = config_class or Config
  193. cls.config_class = config_class
  194. if not root_path:
  195. root_path = PROJECT_DIR
  196. manager = cls(root_path=root_path)
  197. config_type = os.environ.get('MAXKB_CONFIG_TYPE')
  198. if config_type is None or config_type != 'ENV':
  199. manager.load_from_yml()
  200. else:
  201. manager.load_from_env()
  202. config = manager.config
  203. return config