mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
@@ -9,6 +9,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"vue": "^3.5.10",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -337,16 +337,86 @@
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f"
|
||||
integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
axios@^1.7.9:
|
||||
version "1.7.9"
|
||||
resolved "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
||||
integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
call-bind-apply-helpers@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
csstype@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
entities@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||
|
||||
es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-object-atoms@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
|
||||
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.6"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
esbuild@^0.21.3:
|
||||
version "0.21.5"
|
||||
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
|
||||
@@ -381,11 +451,79 @@ estree-walker@^2.0.2:
|
||||
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
follow-redirects@^1.15.6:
|
||||
version "1.15.9"
|
||||
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
|
||||
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c"
|
||||
integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
get-intrinsic@^1.2.6:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044"
|
||||
integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-define-property "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
es-object-atoms "^1.0.0"
|
||||
function-bind "^1.1.2"
|
||||
get-proto "^1.0.0"
|
||||
gopd "^1.2.0"
|
||||
has-symbols "^1.1.0"
|
||||
hasown "^2.0.2"
|
||||
math-intrinsics "^1.1.0"
|
||||
|
||||
get-proto@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
has-symbols@^1.0.3, has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||
|
||||
has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
|
||||
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
||||
dependencies:
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
hasown@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
magic-string@^0.30.11:
|
||||
version "0.30.17"
|
||||
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||
@@ -393,6 +531,23 @@ magic-string@^0.30.11:
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
nanoid@^3.3.8:
|
||||
version "3.3.8"
|
||||
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
|
||||
@@ -412,6 +567,11 @@ postcss@^8.4.43, postcss@^8.4.48:
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
rollup@^4.20.0:
|
||||
version "4.34.7"
|
||||
resolved "https://registry.npmmirror.com/rollup/-/rollup-4.34.7.tgz#e00d8550688a616a3481c6446bb688d4c753ba8f"
|
||||
|
||||
@@ -6,8 +6,10 @@ from manager.http_server import WebUI
|
||||
from aiohttp import web
|
||||
from core.utils.util import get_local_ip
|
||||
|
||||
TAG = __name__
|
||||
|
||||
async def main():
|
||||
setup_logging() # 最先初始化日志
|
||||
logger = setup_logging()
|
||||
config = load_config()
|
||||
|
||||
# 启动 WebSocket 服务器
|
||||
@@ -28,9 +30,9 @@ async def main():
|
||||
await site.start()
|
||||
webui_runner = runner
|
||||
local_ip = get_local_ip()
|
||||
print(f"WebUI server is running at http://{local_ip}:{port}")
|
||||
logger.bind(tag=TAG).info(f"WebUI server is running at http://{local_ip}:{port}")
|
||||
except Exception as e:
|
||||
print(f"Failed to start WebUI server: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to start WebUI server: {e}")
|
||||
|
||||
try:
|
||||
# 等待 WebSocket 服务器运行
|
||||
|
||||
+20
-13
@@ -1,20 +1,27 @@
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
import sys
|
||||
from loguru import logger
|
||||
|
||||
def setup_logging(log_dir='tmp', data_dir='data'):
|
||||
"""配置全局日志"""
|
||||
"""配置全局彩色日志(不同区块不同标签)"""
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler(os.path.join(log_dir, "server.log"), encoding='utf-8')
|
||||
],
|
||||
force=True
|
||||
# 设置日志格式,时间、日志级别、标签、消息
|
||||
log_format = (
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss}</green>"
|
||||
"[<light-blue>{extra[tag]}</light-blue>]"
|
||||
" - <level>{level}</level> - "
|
||||
"<light-green>{message}</light-green>"
|
||||
)
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
# 配置日志输出
|
||||
logger.remove()
|
||||
|
||||
# 输出到控制台
|
||||
logger.add(sys.stdout, format=log_format, level="INFO")
|
||||
|
||||
# 输出到文件
|
||||
logger.add(os.path.join(log_dir, "server.log"), format="{time:YYYY-MM-DD HH:mm:ss} - {name} - {level} - {extra[tag]} - {message}", level="INFO")
|
||||
|
||||
return logger
|
||||
|
||||
+15
-13
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import time
|
||||
import yaml
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from typing import Dict, Any, Optional
|
||||
from copy import deepcopy
|
||||
from core.utils.util import get_project_dir
|
||||
@@ -9,12 +9,14 @@ from core.utils import asr, vad, llm, tts
|
||||
from manager.api.user_manager import UserManager
|
||||
from core.utils.lock_manager import FileLockManager
|
||||
|
||||
TAG = __name__
|
||||
|
||||
class PrivateConfig:
|
||||
def __init__(self, device_id: str, default_config: Dict[str, Any], auth_code_gen=None):
|
||||
self.device_id = device_id
|
||||
self.default_config = default_config
|
||||
self.config_path = get_project_dir() + 'data/.private_config.yaml'
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger = setup_logging()
|
||||
self.private_config = {}
|
||||
self.auth_code_gen = auth_code_gen
|
||||
self.user_manager = UserManager()
|
||||
@@ -74,7 +76,7 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling private config: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error handling private config: {e}")
|
||||
self.private_config = {}
|
||||
|
||||
async def update_config(self, selected_modules: Dict[str, str], prompt: str, nickname: str) -> bool:
|
||||
@@ -129,7 +131,7 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error updating config: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error updating config: {e}")
|
||||
return False
|
||||
|
||||
async def delete_config(self) -> bool:
|
||||
@@ -163,13 +165,13 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error deleting config: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error deleting config: {e}")
|
||||
return False
|
||||
|
||||
def create_private_instances(self):
|
||||
# 判断存在私有配置,并且self.device_id在私有配置中
|
||||
if not self.private_config:
|
||||
logging.error("Private config not found for device_id: %s", self.device_id)
|
||||
self.logger.bind(tag=TAG).error("Private config not found for device_id: {}", self.device_id)
|
||||
return None, None, None, None
|
||||
|
||||
"""创建私有处理模块实例"""
|
||||
@@ -208,7 +210,7 @@ class PrivateConfig:
|
||||
timestamp: 指定的时间戳,不传则使用当前时间
|
||||
"""
|
||||
if not self.private_config:
|
||||
self.logger.error("Private config not found")
|
||||
self.logger.bind(tag=TAG).error("Private config not found")
|
||||
return False
|
||||
|
||||
try:
|
||||
@@ -235,7 +237,7 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error updating last chat time: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error updating last chat time: {e}")
|
||||
return False
|
||||
|
||||
def get_auth_code(self) -> str:
|
||||
@@ -252,7 +254,7 @@ class PrivateConfig:
|
||||
try:
|
||||
# 检查用户是否存在
|
||||
if not self.user_manager.get_user(username):
|
||||
self.logger.error(f"User {username} not found")
|
||||
self.logger.bind(tag=TAG).error(f"User {username} not found")
|
||||
return False
|
||||
|
||||
# 读取所有配置
|
||||
@@ -260,12 +262,12 @@ class PrivateConfig:
|
||||
all_configs = yaml.safe_load(f) or {}
|
||||
|
||||
if self.device_id not in all_configs:
|
||||
self.logger.error(f"Device {self.device_id} not found")
|
||||
self.logger.bind(tag=TAG).error(f"Device {self.device_id} not found")
|
||||
return False
|
||||
|
||||
# 删除认证码
|
||||
auth_code = all_configs[self.device_id].get('auth_code')
|
||||
self.logger.info(f"Binding user {username} to device {self.device_id}")
|
||||
self.logger.bind(tag=TAG).info(f"Binding user {username} to device {self.device_id}")
|
||||
if auth_code:
|
||||
del all_configs[self.device_id]['auth_code']
|
||||
|
||||
@@ -293,7 +295,7 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error binding user: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error binding user: {e}")
|
||||
return False
|
||||
|
||||
async def unbind_user(self) -> bool:
|
||||
@@ -330,7 +332,7 @@ class PrivateConfig:
|
||||
self.lock_manager.release_lock(self.config_path)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error unbinding user: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error unbinding user: {e}")
|
||||
return False
|
||||
|
||||
def get_owner(self) -> Optional[str]:
|
||||
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
|
||||
|
||||
class AuthenticationError(Exception):
|
||||
@@ -37,15 +37,15 @@ class AuthMiddleware:
|
||||
# 验证Authorization header
|
||||
auth_header = headers.get("authorization", "")
|
||||
if not auth_header.startswith("Bearer "):
|
||||
logger.error("Missing or invalid Authorization header")
|
||||
logger.bind(tag=TAG).error("Missing or invalid Authorization header")
|
||||
raise AuthenticationError("Missing or invalid Authorization header")
|
||||
|
||||
token = auth_header.split(" ")[1]
|
||||
if token not in self.tokens:
|
||||
logger.error(f"Invalid token: {token}")
|
||||
logger.bind(tag=TAG).error(f"Invalid token: {token}")
|
||||
raise AuthenticationError("Invalid token")
|
||||
|
||||
logger.info(f"Authentication successful - Device: {device_id}, Token: {self.tokens[token]}")
|
||||
logger.bind(tag=TAG).info(f"Authentication successful - Device: {device_id}, Token: {self.tokens[token]}")
|
||||
return True
|
||||
|
||||
def get_token_name(self, token):
|
||||
|
||||
+30
-28
@@ -4,7 +4,7 @@ import uuid
|
||||
import time
|
||||
import queue
|
||||
import asyncio
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import threading
|
||||
import websockets
|
||||
from typing import Dict, Any
|
||||
@@ -19,10 +19,12 @@ from config.private_config import PrivateConfig
|
||||
from core.auth import AuthMiddleware, AuthenticationError
|
||||
from core.utils.auth_code_gen import AuthCodeGenerator # 添加导入
|
||||
|
||||
TAG = __name__
|
||||
|
||||
class ConnectionHandler:
|
||||
def __init__(self, config: Dict[str, Any], _vad, _asr, _llm, _tts):
|
||||
self.config = config
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger = setup_logging()
|
||||
self.auth = AuthMiddleware(config)
|
||||
|
||||
self.websocket = None
|
||||
@@ -85,7 +87,7 @@ class ConnectionHandler:
|
||||
try:
|
||||
# 获取并验证headers
|
||||
self.headers = dict(ws.request.headers)
|
||||
self.logger.info(f"New connection request - Headers: {self.headers}")
|
||||
self.logger.bind(tag=TAG).info(f"New connection request - Headers: {self.headers}")
|
||||
|
||||
# 进行认证
|
||||
await self.auth.authenticate(self.headers)
|
||||
@@ -94,7 +96,7 @@ class ConnectionHandler:
|
||||
|
||||
# Load private configuration if device_id is provided
|
||||
bUsePrivateConfig = self.config.get("use_private_config", False)
|
||||
logging.info(f"bUsePrivateConfig: {bUsePrivateConfig}, device_id: {device_id}")
|
||||
self.info(f"bUsePrivateConfig: {bUsePrivateConfig}, device_id: {device_id}")
|
||||
if bUsePrivateConfig and device_id:
|
||||
try:
|
||||
self.private_config = PrivateConfig(device_id, self.config, self.auth_code_gen)
|
||||
@@ -112,12 +114,12 @@ class ConnectionHandler:
|
||||
self.asr = asr
|
||||
self.llm = llm
|
||||
self.tts = tts
|
||||
self.logger.info(f"Loaded private config and instances for device {device_id}")
|
||||
self.logger.bind(tag=TAG).info(f"Loaded private config and instances for device {device_id}")
|
||||
else:
|
||||
self.logger.error(f"Failed to create instances for device {device_id}")
|
||||
self.logger.bind(tag=TAG).error(f"Failed to create instances for device {device_id}")
|
||||
self.private_config = None
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error initializing private config: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"Error initializing private config: {e}")
|
||||
self.private_config = None
|
||||
raise
|
||||
|
||||
@@ -138,15 +140,15 @@ class ConnectionHandler:
|
||||
async for message in self.websocket:
|
||||
await self._route_message(message)
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
self.logger.info("客户端断开连接")
|
||||
self.logger.bind(tag=TAG).info("客户端断开连接")
|
||||
await self.close()
|
||||
|
||||
except AuthenticationError as e:
|
||||
self.logger.error(f"Authentication failed: {str(e)}")
|
||||
self.logger.bind(tag=TAG).error(f"Authentication failed: {str(e)}")
|
||||
await ws.close()
|
||||
return
|
||||
except Exception as e:
|
||||
self.logger.error(f"Connection error: {str(e)}")
|
||||
self.logger.bind(tag=TAG).error(f"Connection error: {str(e)}")
|
||||
await ws.close()
|
||||
return
|
||||
|
||||
@@ -208,7 +210,7 @@ class ConnectionHandler:
|
||||
start_time = time.time() # 记录开始时间
|
||||
llm_responses = self.llm.response(self.session_id, self.dialogue.get_llm_dialogue())
|
||||
except Exception as e:
|
||||
self.logger.error(f"LLM 处理出错 {query}: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"LLM 处理出错 {query}: {e}")
|
||||
return None
|
||||
# 提交 TTS 任务到线程池
|
||||
self.llm_finish_task = False
|
||||
@@ -220,7 +222,7 @@ class ConnectionHandler:
|
||||
break
|
||||
|
||||
end_time = time.time() # 记录结束时间
|
||||
self.logger.debug(f"大模型返回时间时间: {end_time - start_time} 秒, 生成token={content}")
|
||||
self.logger.bind(tag=TAG).debug(f"大模型返回时间时间: {end_time - start_time} 秒, 生成token={content}")
|
||||
if is_segment(response_message):
|
||||
segment_text = "".join(response_message[start:])
|
||||
segment_text = get_string_no_punctuation_or_emoji(segment_text)
|
||||
@@ -241,7 +243,7 @@ class ConnectionHandler:
|
||||
self.llm_finish_task = True
|
||||
# 更新对话
|
||||
self.dialogue.put(Message(role="assistant", content="".join(response_message)))
|
||||
self.logger.debug(json.dumps(self.dialogue.get_llm_dialogue(), indent=4, ensure_ascii=False))
|
||||
self.logger.bind(tag=TAG).debug(json.dumps(self.dialogue.get_llm_dialogue(), indent=4, ensure_ascii=False))
|
||||
return True
|
||||
|
||||
def _priority_thread(self):
|
||||
@@ -253,25 +255,25 @@ class ConnectionHandler:
|
||||
continue
|
||||
text = None
|
||||
try:
|
||||
self.logger.debug("正在处理TTS任务...")
|
||||
self.logger.bind(tag=TAG).debug("正在处理TTS任务...")
|
||||
tts_file, text = future.result(timeout=10)
|
||||
if text is None or len(text) <= 0:
|
||||
continue
|
||||
if tts_file is None:
|
||||
self.logger.error(f"TTS文件生成失败: {text}")
|
||||
self.logger.bind(tag=TAG).error(f"TTS文件生成失败: {text}")
|
||||
continue
|
||||
self.logger.debug(f"TTS文件生成完毕,文件路径: {tts_file}")
|
||||
self.logger.bind(tag=TAG).debug(f"TTS文件生成完毕,文件路径: {tts_file}")
|
||||
if os.path.exists(tts_file):
|
||||
opus_datas, duration = self.tts.wav_to_opus_data(tts_file)
|
||||
else:
|
||||
self.logger.error(f"TTS文件不存在: {tts_file}")
|
||||
self.logger.bind(tag=TAG).error(f"TTS文件不存在: {tts_file}")
|
||||
opus_datas = []
|
||||
duration = 0
|
||||
except TimeoutError:
|
||||
self.logger.error("TTS 任务超时")
|
||||
self.logger.bind(tag=TAG).error("TTS 任务超时")
|
||||
continue
|
||||
except Exception as e:
|
||||
self.logger.error(f"TTS 任务出错: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"TTS 任务出错: {e}")
|
||||
continue
|
||||
if not self.client_abort:
|
||||
# 如果没有中途打断就发送语音
|
||||
@@ -281,27 +283,27 @@ class ConnectionHandler:
|
||||
if self.tts.delete_audio_file and os.path.exists(tts_file):
|
||||
os.remove(tts_file)
|
||||
except Exception as e:
|
||||
self.logger.error(f"TTS任务处理错误: {e}")
|
||||
self.logger.bind(tag=TAG).error(f"TTS任务处理错误: {e}")
|
||||
self.clearSpeakStatus()
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self.websocket.send(json.dumps({"type": "tts", "state": "stop", "session_id": self.session_id})),
|
||||
self.loop
|
||||
)
|
||||
self.logger.error(f"tts_priority priority_thread: {text}{e}")
|
||||
self.logger.bind(tag=TAG).error(f"tts_priority priority_thread: {text}{e}")
|
||||
|
||||
def speak_and_play(self, text):
|
||||
if text is None or len(text) <= 0:
|
||||
self.logger.info(f"无需tts转换,query为空,{text}")
|
||||
self.logger.bind(tag=TAG).info(f"无需tts转换,query为空,{text}")
|
||||
return None, text
|
||||
tts_file = self.tts.to_tts(text)
|
||||
if tts_file is None:
|
||||
self.logger.error(f"tts转换失败,{text}")
|
||||
self.logger.bind(tag=TAG).error(f"tts转换失败,{text}")
|
||||
return None, text
|
||||
self.logger.debug(f"TTS 文件生成完毕: {tts_file}")
|
||||
self.logger.bind(tag=TAG).debug(f"TTS 文件生成完毕: {tts_file}")
|
||||
return tts_file, text
|
||||
|
||||
def clearSpeakStatus(self):
|
||||
self.logger.debug(f"清除服务端讲话状态")
|
||||
self.logger.bind(tag=TAG).debug(f"清除服务端讲话状态")
|
||||
self.asr_server_receive = True
|
||||
self.tts_last_text = None
|
||||
self.tts_first_text = None
|
||||
@@ -310,7 +312,7 @@ class ConnectionHandler:
|
||||
|
||||
def recode_first_last_text(self, text):
|
||||
if not self.tts_first_text:
|
||||
self.logger.info(f"大模型说出第一句话: {text}")
|
||||
self.logger.bind(tag=TAG).info(f"大模型说出第一句话: {text}")
|
||||
self.tts_first_text = text
|
||||
self.tts_last_text = text
|
||||
|
||||
@@ -320,14 +322,14 @@ class ConnectionHandler:
|
||||
self.executor.shutdown(wait=False)
|
||||
if self.websocket:
|
||||
await self.websocket.close()
|
||||
self.logger.info("连接资源已释放")
|
||||
self.logger.bind(tag=TAG).info("连接资源已释放")
|
||||
|
||||
def reset_vad_states(self):
|
||||
self.client_audio_buffer = bytes()
|
||||
self.client_have_voice = False
|
||||
self.client_have_voice_last_time = 0
|
||||
self.client_voice_stop = False
|
||||
self.logger.debug("VAD states reset.")
|
||||
self.logger.bind(tag=TAG).debug("VAD states reset.")
|
||||
|
||||
def stop_all_tasks(self):
|
||||
while self.scheduled_tasks:
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import json
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
async def handleAbortMessage(conn):
|
||||
logger.info("Abort message received")
|
||||
logger.bind(tag=TAG).info("Abort message received")
|
||||
# 设置成打断状态,会自动打断llm、tts任务
|
||||
conn.client_abort = True
|
||||
# 打断屏显任务
|
||||
@@ -13,4 +14,4 @@ async def handleAbortMessage(conn):
|
||||
# 打断客户端说话状态
|
||||
await conn.websocket.send(json.dumps({"type": "tts", "state": "stop", "session_id": conn.session_id}))
|
||||
conn.clearSpeakStatus()
|
||||
logger.info("Abort message received-end")
|
||||
logger.bind(tag=TAG).info("Abort message received-end")
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import json
|
||||
import asyncio
|
||||
import time
|
||||
from core.utils.util import remove_punctuation_and_length, get_string_no_punctuation_or_emoji
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
async def handleAudioMessage(conn, audio):
|
||||
if not conn.asr_server_receive:
|
||||
logger.debug(f"前期数据处理中,暂停接收")
|
||||
logger = setup_logging()
|
||||
logger.bind(tag=TAG).debug(f"前期数据处理中,暂停接收")
|
||||
return
|
||||
if conn.client_listen_mode == "auto":
|
||||
have_voice = conn.vad.is_vad(conn, audio)
|
||||
@@ -28,7 +30,7 @@ async def handleAudioMessage(conn, audio):
|
||||
conn.client_abort = False
|
||||
conn.asr_server_receive = False
|
||||
text, file_path = conn.asr.speech_to_text(conn.asr_audio, conn.session_id)
|
||||
logger.info(f"识别文本: {text}")
|
||||
logger.bind(tag=TAG).info(f"识别文本: {text}")
|
||||
text_len, text_without_punctuation = remove_punctuation_and_length(text)
|
||||
if text_len <= conn.max_cmd_length and await handleCMDMessage(conn, text_without_punctuation):
|
||||
return
|
||||
@@ -44,7 +46,7 @@ async def handleCMDMessage(conn, text):
|
||||
cmd_exit = conn.cmd_exit
|
||||
for cmd in cmd_exit:
|
||||
if text == cmd:
|
||||
logger.info("识别到明确的退出命令".format(text))
|
||||
logger.bind(tag=TAG).info("识别到明确的退出命令".format(text))
|
||||
await finishToChat(conn)
|
||||
return True
|
||||
return False
|
||||
@@ -80,7 +82,7 @@ async def sendAudioMessage(conn, audios, duration, text):
|
||||
|
||||
# 发送 tts.start
|
||||
if text == conn.tts_first_text:
|
||||
logger.info(f"发送第一段语音: {text}")
|
||||
logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
|
||||
conn.tts_start_speak_time = time.time()
|
||||
|
||||
# 发送 sentence_start(每个音频文件之前发送一次)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
async def handleHelloMessage(conn):
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import json
|
||||
from core.handle.abortHandle import handleAbortMessage
|
||||
from core.handle.helloHandle import handleHelloMessage
|
||||
from core.handle.audioHandle import startToChat
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
async def handleTextMessage(conn, message):
|
||||
"""处理文本消息"""
|
||||
logger.info(f"收到文本消息:{message}")
|
||||
logger.bind(tag=TAG).info(f"收到文本消息:{message}")
|
||||
try:
|
||||
msg_json = json.loads(message)
|
||||
if isinstance(msg_json, int):
|
||||
@@ -22,7 +23,7 @@ async def handleTextMessage(conn, message):
|
||||
elif msg_json["type"] == "listen":
|
||||
if "mode" in msg_json:
|
||||
conn.client_listen_mode = msg_json["mode"]
|
||||
logger.debug(f"客户端拾音模式:{conn.client_listen_mode}")
|
||||
logger.bind(tag=TAG).debug(f"客户端拾音模式:{conn.client_listen_mode}")
|
||||
if msg_json["state"] == "start":
|
||||
conn.client_have_voice = True
|
||||
conn.client_voice_stop = False
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import requests
|
||||
import json
|
||||
import re
|
||||
from core.providers.llm.base import LLMProviderBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
# 定义用于匹配中文标点符号的正则表达式(包括句号、感叹号、问号、分号)
|
||||
punctuation_pattern = re.compile(r'([。!?;])')
|
||||
@@ -27,7 +28,7 @@ class LLMProvider(LLMProviderBase):
|
||||
"query": last_msg["content"],
|
||||
"stream": True
|
||||
}
|
||||
logger.info(f"发送到 Coze API 的请求数据: {json.dumps(data, ensure_ascii=False)}")
|
||||
logger.bind(tag=TAG).info(f"发送到 Coze API 的请求数据: {json.dumps(data, ensure_ascii=False)}")
|
||||
|
||||
headers = {
|
||||
'Authorization': f'Bearer {self.personal_access_token}',
|
||||
@@ -43,7 +44,7 @@ class LLMProvider(LLMProviderBase):
|
||||
json=data,
|
||||
stream=True
|
||||
)
|
||||
logger.info(f"请求状态: {response.status_code}")
|
||||
logger.bind(tag=TAG).info(f"请求状态: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
# 对每一行流数据进行处理,不做跨块累积
|
||||
@@ -54,7 +55,7 @@ class LLMProvider(LLMProviderBase):
|
||||
# 使用 utf-8 解码,错误部分用替换符
|
||||
line = line_bytes.decode('utf-8', errors='replace')
|
||||
except Exception as e:
|
||||
logger.error(f"解码失败: {e}")
|
||||
logger.bind(tag=TAG).error(f"解码失败: {e}")
|
||||
continue
|
||||
if line.startswith("data:"):
|
||||
data_str = line[len("data:"):].strip()
|
||||
@@ -63,7 +64,7 @@ class LLMProvider(LLMProviderBase):
|
||||
try:
|
||||
data_chunk = json.loads(data_str)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON解析失败: {e} 数据: {line}")
|
||||
logger.bind(tag=TAG).error(f"JSON解析失败: {e} 数据: {line}")
|
||||
continue
|
||||
msg = data_chunk.get("message", {})
|
||||
if msg.get("role") == "assistant" and msg.get("type") == "answer":
|
||||
@@ -88,8 +89,8 @@ class LLMProvider(LLMProviderBase):
|
||||
if content.strip():
|
||||
yield content.strip()
|
||||
else:
|
||||
logger.error(f"请求失败,状态码: {response.status_code}")
|
||||
logger.bind(tag=TAG).error(f"请求失败,状态码: {response.status_code}")
|
||||
yield f"【Coze服务响应异常:请求失败,状态码 {response.status_code}】"
|
||||
except Exception as e:
|
||||
logger.error(f"Error in Coze response generation: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error in Coze response generation: {e}")
|
||||
yield "【Coze服务响应异常】"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import json
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import requests
|
||||
from core.providers.llm.base import LLMProviderBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class LLMProvider(LLMProviderBase):
|
||||
def __init__(self, config):
|
||||
@@ -35,5 +35,5 @@ class LLMProvider(LLMProviderBase):
|
||||
yield event['answer']
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in response generation: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
|
||||
yield "【服务响应异常】"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import google.generativeai as genai
|
||||
from core.providers.llm.base import LLMProviderBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class LLMProvider(LLMProviderBase):
|
||||
def __init__(self, config):
|
||||
@@ -11,7 +12,7 @@ class LLMProvider(LLMProviderBase):
|
||||
self.api_key = config.get("api_key")
|
||||
|
||||
if not self.api_key or "你" in self.api_key:
|
||||
logger.error("你还没配置Gemini LLM的密钥,请在配置文件中配置密钥,否则无法正常工作")
|
||||
logger.bind(tag=TAG).error("你还没配置Gemini LLM的密钥,请在配置文件中配置密钥,否则无法正常工作")
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -28,7 +29,7 @@ class LLMProvider(LLMProviderBase):
|
||||
}
|
||||
self.chat = None
|
||||
except Exception as e:
|
||||
logger.error(f"Gemini初始化失败: {e}")
|
||||
logger.bind(tag=TAG).error(f"Gemini初始化失败: {e}")
|
||||
self.model = None
|
||||
|
||||
def response(self, session_id, dialogue):
|
||||
@@ -69,7 +70,7 @@ class LLMProvider(LLMProviderBase):
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
logger.error(f"Gemini响应生成错误: {error_msg}")
|
||||
logger.bind(tag=TAG).error(f"Gemini响应生成错误: {error_msg}")
|
||||
|
||||
# 针对不同错误返回友好提示
|
||||
if "Rate limit" in error_msg:
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import requests, json
|
||||
from core.providers.llm.base import LLMProviderBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class LLMProvider(LLMProviderBase):
|
||||
@@ -41,5 +42,5 @@ class LLMProvider(LLMProviderBase):
|
||||
yield json_response["response"]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in Ollama response generation: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error in Ollama response generation: {e}")
|
||||
yield "【Ollama服务响应异常】"
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import openai
|
||||
from core.providers.llm.base import LLMProviderBase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class LLMProvider(LLMProviderBase):
|
||||
@@ -14,7 +15,7 @@ class LLMProvider(LLMProviderBase):
|
||||
else:
|
||||
self.base_url = config.get("url")
|
||||
if "你" in self.api_key:
|
||||
logger.error("你还没配置LLM的密钥,请在配置文件中配置密钥,否则无法正常工作")
|
||||
logger.bind(tag=TAG).error("你还没配置LLM的密钥,请在配置文件中配置密钥,否则无法正常工作")
|
||||
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
||||
|
||||
def response(self, session_id, dialogue):
|
||||
@@ -32,4 +33,4 @@ class LLMProvider(LLMProviderBase):
|
||||
if content: # 仅在content非空时生成
|
||||
yield content
|
||||
except Exception as e:
|
||||
logger.error(f"Error in response generation: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import os
|
||||
import numpy as np
|
||||
import opuslib
|
||||
from pydub import AudioSegment
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
class TTSProviderBase(ABC):
|
||||
@@ -26,14 +27,14 @@ class TTSProviderBase(ABC):
|
||||
asyncio.run(self.text_to_speak(text, tmp_file))
|
||||
if not os.path.exists(tmp_file):
|
||||
max_repeat_time = max_repeat_time - 1
|
||||
logger.error(f"语音生成失败: {text}:{tmp_file},再试{max_repeat_time}次")
|
||||
logger.bind(tag=TAG).error(f"语音生成失败: {text}:{tmp_file},再试{max_repeat_time}次")
|
||||
|
||||
if max_repeat_time > 0:
|
||||
logger.info(f"语音生成成功: {text}:{tmp_file},重试{5 - max_repeat_time}次")
|
||||
logger.bind(tag=TAG).info(f"语音生成成功: {text}:{tmp_file},重试{5 - max_repeat_time}次")
|
||||
|
||||
return tmp_file
|
||||
except Exception as e:
|
||||
logger.info(f"Failed to generate TTS file: {e}")
|
||||
logger.bind(tag=TAG).info(f"Failed to generate TTS file: {e}")
|
||||
return None
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -3,10 +3,12 @@ import uuid
|
||||
import json
|
||||
import base64
|
||||
import requests
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from datetime import datetime
|
||||
from core.providers.tts.base import TTSProviderBase
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class TTSProvider(TTSProviderBase):
|
||||
def __init__(self, config, delete_audio_file):
|
||||
@@ -62,4 +64,4 @@ class TTSProvider(TTSProviderBase):
|
||||
with open(output_file, "wb") as file:
|
||||
file.write(resp.content)
|
||||
else:
|
||||
logger.error(f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}")
|
||||
logger.bind(tag=TAG).error(f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}")
|
||||
|
||||
+9
-9
@@ -2,7 +2,7 @@ import time
|
||||
import wave
|
||||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from typing import Optional, Tuple, List
|
||||
import uuid
|
||||
|
||||
@@ -10,8 +10,8 @@ import opuslib
|
||||
from funasr import AutoModel
|
||||
from funasr.utils.postprocess_utils import rich_transcription_postprocess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class ASR(ABC):
|
||||
@abstractmethod
|
||||
@@ -55,7 +55,7 @@ class FunASR(ASR):
|
||||
pcm_frame = decoder.decode(opus_packet, 960) # 960 samples = 60ms
|
||||
pcm_data.append(pcm_frame)
|
||||
except opuslib.OpusError as e:
|
||||
logger.error(f"Opus解码错误: {e}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Opus解码错误: {e}", exc_info=True)
|
||||
|
||||
with wave.open(file_path, "wb") as wf:
|
||||
wf.setnchannels(1)
|
||||
@@ -72,7 +72,7 @@ class FunASR(ASR):
|
||||
# 保存音频文件
|
||||
start_time = time.time()
|
||||
file_path = self.save_audio_to_file(opus_data, session_id)
|
||||
logger.debug(f"音频文件保存耗时: {time.time() - start_time:.3f}s | 路径: {file_path}")
|
||||
logger.bind(tag=TAG).debug(f"音频文件保存耗时: {time.time() - start_time:.3f}s | 路径: {file_path}")
|
||||
|
||||
# 语音识别
|
||||
start_time = time.time()
|
||||
@@ -84,12 +84,12 @@ class FunASR(ASR):
|
||||
batch_size_s=60,
|
||||
)
|
||||
text = rich_transcription_postprocess(result[0]["text"])
|
||||
logger.debug(f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {text}")
|
||||
logger.bind(tag=TAG).debug(f"语音识别耗时: {time.time() - start_time:.3f}s | 结果: {text}")
|
||||
|
||||
return text, file_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"语音识别失败: {e}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"语音识别失败: {e}", exc_info=True)
|
||||
return None, None
|
||||
|
||||
finally:
|
||||
@@ -97,9 +97,9 @@ class FunASR(ASR):
|
||||
if self.delete_audio_file and file_path and os.path.exists(file_path):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
logger.debug(f"已删除临时音频文件: {file_path}")
|
||||
logger.bind(tag=TAG).debug(f"已删除临时音频文件: {file_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"文件删除失败: {file_path} | 错误: {e}")
|
||||
logger.bind(tag=TAG).error(f"文件删除失败: {file_path} | 错误: {e}")
|
||||
|
||||
|
||||
def create_instance(class_name: str, *args, **kwargs) -> ASR:
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import importlib
|
||||
from datetime import datetime
|
||||
from core.utils.util import is_segment
|
||||
from core.utils.util import get_string_no_punctuation_or_emoji
|
||||
from core.utils.util import read_config, get_project_dir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
def create_instance(class_name, *args, **kwargs):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import asyncio
|
||||
from typing import Dict
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class FileLockManager:
|
||||
_instance = None
|
||||
@@ -25,7 +26,7 @@ class FileLockManager:
|
||||
"""获取锁"""
|
||||
lock = cls.get_lock(file_path)
|
||||
await lock.acquire()
|
||||
logger.debug(f"Acquired lock for {file_path}")
|
||||
logger.bind(tag=TAG).debug(f"Acquired lock for {file_path}")
|
||||
|
||||
@classmethod
|
||||
def release_lock(cls, file_path: str):
|
||||
@@ -33,6 +34,6 @@ class FileLockManager:
|
||||
if file_path in cls._locks:
|
||||
try:
|
||||
cls._locks[file_path].release()
|
||||
logger.debug(f"Released lock for {file_path}")
|
||||
logger.bind(tag=TAG).debug(f"Released lock for {file_path}")
|
||||
except RuntimeError as e:
|
||||
logger.warning(f"Failed to release lock for {file_path}: {e}")
|
||||
logger.bind(tag=TAG).warning(f"Failed to release lock for {file_path}: {e}")
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import importlib
|
||||
from datetime import datetime
|
||||
from core.utils.util import read_config, get_project_dir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = setup_logging()
|
||||
|
||||
|
||||
def create_instance(class_name, *args, **kwargs):
|
||||
|
||||
+6
-6
@@ -1,12 +1,12 @@
|
||||
from abc import ABC, abstractmethod
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
import opuslib
|
||||
import time
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class VAD(ABC):
|
||||
@abstractmethod
|
||||
@@ -17,7 +17,7 @@ class VAD(ABC):
|
||||
|
||||
class SileroVAD(VAD):
|
||||
def __init__(self, config):
|
||||
logger.info("SileroVAD", config)
|
||||
logger.bind(tag=TAG).info("SileroVAD", config)
|
||||
self.model, self.utils = torch.hub.load(repo_or_dir=config["model_dir"],
|
||||
source='local',
|
||||
model='silero_vad',
|
||||
@@ -60,9 +60,9 @@ class SileroVAD(VAD):
|
||||
|
||||
return client_have_voice
|
||||
except opuslib.OpusError as e:
|
||||
logger.info(f"解码错误: {e}")
|
||||
logger.bind(tag=TAG).info(f"解码错误: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing audio packet: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error processing audio packet: {e}")
|
||||
|
||||
|
||||
def create_instance(class_name, *args, **kwargs) -> VAD:
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import asyncio
|
||||
import websockets
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from core.connection import ConnectionHandler
|
||||
from core.utils.util import get_local_ip
|
||||
from core.utils import asr, vad, llm, tts
|
||||
|
||||
TAG = __name__
|
||||
|
||||
class WebSocketServer:
|
||||
def __init__(self, config: dict):
|
||||
self.config = config
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger = setup_logging()
|
||||
self._vad, self._asr, self._llm, self._tts = self._create_processing_instances()
|
||||
|
||||
def _create_processing_instances(self):
|
||||
@@ -46,9 +47,8 @@ class WebSocketServer:
|
||||
host = server_config["ip"]
|
||||
port = server_config["port"]
|
||||
|
||||
self.logger.info("=======下面的地址是websocket协议地址,请勿用浏览器访问=======")
|
||||
self.logger.info("Server is running at ws://%s:%s", get_local_ip(), port)
|
||||
self.logger.info("=======上面的地址是websocket协议地址,请勿用浏览器访问=======")
|
||||
self.logger.bind(tag=TAG).info("Server is running at ws://{}:{}", get_local_ip(), port)
|
||||
self.logger.bind(tag=TAG).info("=======上面的地址是websocket协议地址,请勿用浏览器访问=======")
|
||||
async with websockets.serve(
|
||||
self._handle_connection,
|
||||
host,
|
||||
|
||||
+12
-11
@@ -1,13 +1,14 @@
|
||||
import os
|
||||
import yaml
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from aiohttp import web
|
||||
from core.utils.util import get_project_dir
|
||||
from config.private_config import PrivateConfig
|
||||
from manager.api.user_manager import UserManager # 添加导入
|
||||
from core.utils.auth_code_gen import AuthCodeGenerator # 添加导入
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class ConfigHandler:
|
||||
def __init__(self, session_manager):
|
||||
@@ -42,7 +43,7 @@ class ConfigHandler:
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting module options: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Error getting module options: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '获取配置选项失败'
|
||||
@@ -52,11 +53,11 @@ class ConfigHandler:
|
||||
"""只返回用户绑定的设备配置"""
|
||||
try:
|
||||
username = request['username']
|
||||
logger.info(f"Getting devices for user: {username}")
|
||||
logger.bind(tag=TAG).info(f"Getting devices for user: {username}")
|
||||
|
||||
# 从用户管理器获取用户的设备列表
|
||||
user_devices = await self.user_manager.get_user_devices(username)
|
||||
logger.info(f"User {username} has devices: {user_devices}")
|
||||
logger.bind(tag=TAG).info(f"User {username} has devices: {user_devices}")
|
||||
|
||||
# 读取所有配置
|
||||
all_configs = {}
|
||||
@@ -71,7 +72,7 @@ class ConfigHandler:
|
||||
if device_id in user_devices
|
||||
}
|
||||
|
||||
logger.info(f"Returning {len(user_configs)} device configs for user {username}")
|
||||
logger.bind(tag=TAG).info(f"Returning {len(user_configs)} device configs for user {username}")
|
||||
return web.json_response({
|
||||
'success': True,
|
||||
'data': user_configs,
|
||||
@@ -79,7 +80,7 @@ class ConfigHandler:
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting devices for user {request.get('username')}: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Error getting devices for user {request.get('username')}: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': f'获取设备列表失败: {str(e)}'
|
||||
@@ -101,7 +102,7 @@ class ConfigHandler:
|
||||
'message': '无权操作此设备'
|
||||
}, status=403)
|
||||
|
||||
logger.info(f"Device config updated: {device_id} :\n{config}")
|
||||
logger.bind(tag=TAG).info(f"Device config updated: {device_id} :\n{config}")
|
||||
if not device_id or not config:
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
@@ -128,7 +129,7 @@ class ConfigHandler:
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving device config: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Error saving device config: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': f'保存配置失败: {str(e)}'
|
||||
@@ -163,7 +164,7 @@ class ConfigHandler:
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting device config: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Error deleting device config: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': f'删除配置失败: {str(e)}'
|
||||
@@ -219,7 +220,7 @@ class ConfigHandler:
|
||||
}, status=500)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error binding device: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Error binding device: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': f'绑定设备失败: {str(e)}'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from aiohttp import web
|
||||
import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class LoginHandler:
|
||||
def __init__(self, user_manager, session_manager):
|
||||
@@ -17,7 +18,7 @@ class LoginHandler:
|
||||
password = data.get('password')
|
||||
|
||||
if not username or not password:
|
||||
logger.warning(f"Login attempt with empty credentials from {request.remote}")
|
||||
logger.bind(tag=TAG).warning(f"Login attempt with empty credentials from {request.remote}")
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '用户名和密码不能为空'
|
||||
@@ -25,7 +26,7 @@ class LoginHandler:
|
||||
|
||||
stored_user = await self.user_manager.get_user(username)
|
||||
if not stored_user or stored_user['password'] != self.user_manager.hash_password(password):
|
||||
logger.warning(f"Failed login attempt for user {username} from {request.remote}")
|
||||
logger.bind(tag=TAG).warning(f"Failed login attempt for user {username} from {request.remote}")
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '用户名或密码错误'
|
||||
@@ -45,7 +46,7 @@ class LoginHandler:
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Login error: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Login error: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '登录失败,请稍后重试'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from aiohttp import web
|
||||
from config.settings import update_config
|
||||
from ruamel.yaml.scalarstring import PreservedScalarString
|
||||
from manager.api.auth import verify_token
|
||||
from manager.api.response import response_unauthorized, response_success, response_error
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class PromptApi:
|
||||
def __init__(self, config):
|
||||
@@ -38,5 +38,5 @@ class PromptApi:
|
||||
|
||||
return response_success()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update prompt: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to update prompt: {e}")
|
||||
return response_error(str(e))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from aiohttp import web
|
||||
import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class RegisterHandler:
|
||||
def __init__(self, user_manager):
|
||||
@@ -17,7 +17,7 @@ class RegisterHandler:
|
||||
password = data.get('password')
|
||||
|
||||
if not username or not password:
|
||||
logger.warning(f"Registration attempt with empty credentials from {request.remote}")
|
||||
logger.bind(tag=TAG).warning(f"Registration attempt with empty credentials from {request.remote}")
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '用户名和密码不能为空'
|
||||
@@ -25,7 +25,7 @@ class RegisterHandler:
|
||||
|
||||
# 检查用户是否已存在
|
||||
if await self.user_manager.get_user(username):
|
||||
logger.warning(f"Registration attempt with existing username {username} from {request.remote}")
|
||||
logger.bind(tag=TAG).warning(f"Registration attempt with existing username {username} from {request.remote}")
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '用户名已存在'
|
||||
@@ -41,14 +41,14 @@ class RegisterHandler:
|
||||
}
|
||||
await self.user_manager.add_user(username, user_data)
|
||||
|
||||
logger.info(f"Successfully registered new user {username} from {request.remote}")
|
||||
logger.bind(tag=TAG).info(f"Successfully registered new user {username} from {request.remote}")
|
||||
return web.json_response({
|
||||
'success': True,
|
||||
'message': '注册成功'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Register error: {str(e)}", exc_info=True)
|
||||
logger.bind(tag=TAG).error(f"Register error: {str(e)}", exc_info=True)
|
||||
return web.json_response({
|
||||
'success': False,
|
||||
'message': '注册失败,请稍后重试'
|
||||
|
||||
+15
-14
@@ -1,11 +1,12 @@
|
||||
import os
|
||||
import yaml
|
||||
import hashlib
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from core.utils.util import get_project_dir
|
||||
from core.utils.lock_manager import FileLockManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
class UserManager:
|
||||
def __init__(self):
|
||||
@@ -23,9 +24,9 @@ class UserManager:
|
||||
with open(self.secrets_path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(default_config, f)
|
||||
os.chmod(self.secrets_path, 0o600)
|
||||
logger.info("Created new .secrets.yaml file")
|
||||
logger.bind(tag=TAG).info("Created new .secrets.yaml file")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create .secrets.yaml: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to create .secrets.yaml: {e}")
|
||||
raise
|
||||
|
||||
async def _load_user_data_internal(self):
|
||||
@@ -34,9 +35,9 @@ class UserManager:
|
||||
with open(self.secrets_path, 'r', encoding='utf-8') as f:
|
||||
data = yaml.safe_load(f) or {'users': {}}
|
||||
users = data['users']
|
||||
logger.debug("Successfully loaded user data")
|
||||
logger.bind(tag=TAG).debug("Successfully loaded user data")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load user data: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to load user data: {e}")
|
||||
users = {}
|
||||
return users
|
||||
|
||||
@@ -50,7 +51,7 @@ class UserManager:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load user data: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to load user data: {e}")
|
||||
users = {}
|
||||
return users
|
||||
|
||||
@@ -59,9 +60,9 @@ class UserManager:
|
||||
try:
|
||||
with open(self.secrets_path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump({'users': users}, f)
|
||||
logger.debug("Successfully saved user data")
|
||||
logger.bind(tag=TAG).debug("Successfully saved user data")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save user data: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to save user data: {e}")
|
||||
raise
|
||||
|
||||
async def save_user_data(self, users):
|
||||
@@ -73,7 +74,7 @@ class UserManager:
|
||||
finally:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save user data: {e}")
|
||||
logger.bind(tag=TAG).error(f"Failed to save user data: {e}")
|
||||
raise
|
||||
|
||||
def hash_password(self, password):
|
||||
@@ -103,7 +104,7 @@ class UserManager:
|
||||
finally:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding user: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error adding user: {e}")
|
||||
raise
|
||||
|
||||
async def update_user(self, username, data):
|
||||
@@ -120,7 +121,7 @@ class UserManager:
|
||||
finally:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating user: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error updating user: {e}")
|
||||
return False
|
||||
|
||||
async def get_user_devices(self, username: str) -> list:
|
||||
@@ -151,7 +152,7 @@ class UserManager:
|
||||
finally:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding device: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error adding device: {e}")
|
||||
return False
|
||||
|
||||
async def remove_device(self, username: str, device_id: str) -> bool:
|
||||
@@ -170,5 +171,5 @@ class UserManager:
|
||||
finally:
|
||||
self.lock_manager.release_lock(self.secrets_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Error removing device: {e}")
|
||||
logger.bind(tag=TAG).error(f"Error removing device: {e}")
|
||||
return False
|
||||
|
||||
@@ -5,10 +5,9 @@ current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
root_dir = os.path.dirname(current_dir)
|
||||
sys.path.append(root_dir)
|
||||
|
||||
import logging
|
||||
from config.logger import setup_logging
|
||||
from aiohttp import web
|
||||
from aiohttp_cors import setup as cors_setup, ResourceOptions
|
||||
from core.utils.util import get_local_ip
|
||||
from manager.api.login import LoginHandler
|
||||
from manager.api.register import RegisterHandler
|
||||
from manager.api.user_manager import UserManager
|
||||
@@ -16,7 +15,8 @@ from manager.api.config import ConfigHandler
|
||||
from manager.session import SessionManager
|
||||
from functools import wraps
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
def auth_required(handler):
|
||||
"""鉴权装饰器"""
|
||||
@@ -94,23 +94,21 @@ class WebUI:
|
||||
# 从请求头获取session_id
|
||||
session_id = request.headers.get('Authorization')
|
||||
if not session_id:
|
||||
logger.warning("No session_id in Authorization header")
|
||||
logger.bind(tag=TAG).warning("No session_id in Authorization header")
|
||||
return web.json_response({'error': 'Unauthorized'}, status=401)
|
||||
|
||||
username = self.session_manager.validate_session(session_id)
|
||||
if not username:
|
||||
logger.warning(f"Invalid session_id: {session_id}")
|
||||
logger.bind(tag=TAG).warning(f"Invalid session_id: {session_id}")
|
||||
return web.json_response({'error': 'Unauthorized'}, status=401)
|
||||
|
||||
request['username'] = username
|
||||
logger.debug(f"Auth success for user: {username}")
|
||||
logger.bind(tag=TAG).debug(f"Auth success for user: {username}")
|
||||
return await handler(request)
|
||||
return wrapper
|
||||
|
||||
def run(self, host='0.0.0.0', port=8002):
|
||||
"""运行服务器"""
|
||||
local_ip = get_local_ip()
|
||||
logger.info(f"WebUI server is running at http://{local_ip}:{port}")
|
||||
web.run_app(self.app, host=host, port=port)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Generated
+47
-2
@@ -539,7 +539,7 @@ description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["main"]
|
||||
markers = "platform_system == \"Windows\""
|
||||
markers = "platform_system == \"Windows\" or sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
@@ -1813,6 +1813,30 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.7.3"
|
||||
description = "Python logging made (stupidly) simple"
|
||||
optional = false
|
||||
python-versions = ">=3.5,<4.0"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"},
|
||||
{file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.2"
|
||||
@@ -4241,6 +4265,27 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.2.0"
|
||||
description = "A small Python utility to set file creation time on Windows"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
groups = ["main"]
|
||||
markers = "sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"},
|
||||
{file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.18.3"
|
||||
@@ -4346,4 +4391,4 @@ reference = "mirrors"
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.10.16"
|
||||
content-hash = "9e62f0425b6f332e766fb61ceb2b0d8e49a9ba4dc439e181ba709fa8436ad482"
|
||||
content-hash = "8645b79840ee30a5262cc87a54aff45a21756f37d6e15fec3215e342ebfe864d"
|
||||
|
||||
@@ -25,6 +25,7 @@ aiohttp-cors = "0.7.0"
|
||||
ormsgpack = "1.7.0"
|
||||
ruamel-yaml = "0.18.10"
|
||||
setuptools = "^75.8.0"
|
||||
loguru = "^0.7.3"
|
||||
|
||||
|
||||
[[tool.poetry.source]]
|
||||
|
||||
Reference in New Issue
Block a user