添加服务发现接口、支持跨域

This commit is contained in:
John Smith
2024-11-02 23:22:54 +08:00
parent baa64f3dc5
commit 132d2a9f71
7 changed files with 96 additions and 14 deletions

View File

@@ -4,18 +4,37 @@ from typing import *
import tornado.web
import config
class ApiHandler(tornado.web.RequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.json_args: Optional[dict] = None
def prepare(self):
def set_default_headers(self):
self.set_header('Cache-Control', 'no-cache')
self.add_header('Vary', 'Origin')
origin = self.request.headers.get('Origin', None)
if origin is None:
return
cfg = config.get_config()
if not cfg.is_allowed_cors_origin(origin):
return
self.set_header('Access-Control-Allow-Origin', origin)
self.set_header('Access-Control-Allow-Methods', '*')
self.set_header('Access-Control-Allow-Headers', '*')
self.set_header('Access-Control-Max-Age', '3600')
def prepare(self):
if not self.request.headers.get('Content-Type', '').startswith('application/json'):
return
try:
self.json_args = json.loads(self.request.body)
except json.JSONDecodeError:
pass
async def options(self, *_args, **_kwargs):
self.set_status(204)

View File

@@ -223,12 +223,13 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
self._refresh_receive_timeout_timer()
# 跨域测试用
def check_origin(self, origin):
cfg = config.get_config()
if cfg.debug:
return True
return super().check_origin(origin)
return (
cfg.debug # 开发时前端localhost直连
or cfg.is_allowed_cors_origin(origin)
or super().check_origin(origin) # 和Host相同
)
@property
def has_joined_room(self):

View File

@@ -52,6 +52,14 @@ class ServerInfoHandler(api.base.ApiHandler):
})
class ServiceDiscoveryHandler(api.base.ApiHandler):
async def get(self):
cfg = config.get_config()
self.write({
'endpoints': cfg.registered_endpoints,
})
class UploadEmoticonHandler(api.base.ApiHandler):
async def post(self):
cfg = config.get_config()
@@ -94,6 +102,7 @@ class NoCacheStaticFileHandler(tornado.web.StaticFileHandler):
ROUTES = [
(r'/api/server_info', ServerInfoHandler),
(r'/api/endpoints', ServiceDiscoveryHandler),
(r'/api/emoticon', UploadEmoticonHandler),
]
# 通配的放在最后

View File

@@ -169,6 +169,9 @@ class _OpenLiveHandlerBase(api.base.ApiHandler):
def prepare(self):
super().prepare()
if self.request.method == 'OPTIONS':
return
if not isinstance(self.json_args, dict):
raise tornado.web.MissingArgumentError('body')

View File

@@ -24,6 +24,7 @@ class _AdminHandlerBase(api.base.ApiHandler):
if not cfg.enable_admin_plugins:
raise tornado.web.HTTPError(403)
if self.request.method != 'OPTIONS':
logger.info('client=%s requesting admin plugin, cls=%s', self.request.remote_ip, type(self).__name__)
super().prepare()

View File

@@ -2,6 +2,7 @@
import configparser
import logging
import os
import re
from typing import *
logger = logging.getLogger(__name__)
@@ -30,7 +31,7 @@ def init(cmd_args):
_config = config
def reload(cmd_args):
def reload(cmd_args=None):
config_path = ''
for path in CONFIG_PATH_LIST:
if os.path.exists(path):
@@ -73,12 +74,15 @@ class AppConfig:
self.open_live_app_id = 0
self.enable_translate = True
self.allow_translate_rooms = set()
self.allow_translate_rooms: Set[int] = set()
self.translate_max_queue_size = 10
self.translation_cache_size = 50000
self.translator_configs = []
self.translator_configs: List[dict] = []
self.text_emoticons = []
self.text_emoticons: List[dict] = []
self.registered_endpoints: List[str] = []
self.cors_origins: List[re.Pattern[str]] = []
@property
def is_open_live_configured(self):
@@ -86,7 +90,9 @@ class AppConfig:
self.open_live_access_key_id != '' and self.open_live_access_key_secret != '' and self.open_live_app_id != 0
)
def load_cmd_args(self, args):
def load_cmd_args(self, args=None):
if args is None:
return
if args.host is not None:
self.host = args.host
if args.port is not None:
@@ -101,6 +107,8 @@ class AppConfig:
self._load_app_config(config)
self._load_translator_configs(config)
self._load_text_emoticons(config)
self._load_registered_endpoints(config)
self._load_cors_origins(config)
except Exception: # noqa
logger.exception('Failed to load config:')
return False
@@ -145,7 +153,10 @@ class AppConfig:
return url
def _load_translator_configs(self, config: configparser.ConfigParser):
try:
app_section = config['app']
except KeyError:
return
section_names = _str_to_list(app_section.get('translator_configs', ''))
translator_configs = []
for section_name in section_names:
@@ -191,13 +202,41 @@ class AppConfig:
self.translator_configs = translator_configs
def _load_text_emoticons(self, config: configparser.ConfigParser):
try:
mappings_section = config['text_emoticon_mappings']
except KeyError:
return
text_emoticons = []
for value in mappings_section.values():
keyword, _, url = value.partition(',')
text_emoticons.append({'keyword': keyword, 'url': url})
self.text_emoticons = text_emoticons
def _load_registered_endpoints(self, config: configparser.ConfigParser):
try:
registered_endpoints_section = config['registered_endpoints']
except KeyError:
return
registered_endpoints = list(registered_endpoints_section.values())
self.registered_endpoints = registered_endpoints
def _load_cors_origins(self, config: configparser.ConfigParser):
try:
cors_origins_section = config['cors_origins']
except KeyError:
return
cors_origins = [
re.compile(origin, re.IGNORECASE)
for origin in cors_origins_section.values()
]
self.cors_origins = cors_origins
def is_allowed_cors_origin(self, origin):
return any(
pattern.fullmatch(origin) is not None
for pattern in self.cors_origins
)
def _str_to_list(value, item_type: Type = str, container_type: Type = list):
value = value.strip()
@@ -206,5 +245,5 @@ def _str_to_list(value, item_type: Type = str, container_type: Type = list):
items = value.split(',')
items = map(lambda item: item.strip(), items)
if item_type is not str:
items = map(lambda item: item_type(item), items)
items = map(item_type, items)
return container_type(items)

View File

@@ -266,3 +266,13 @@ temperature = 0.4
80 = [抱拳],http://i0.hdslb.com/bfs/live/3f170894dd08827ee293afcb5a3d2b60aecdb5b1.png
81 = [给力],http://i0.hdslb.com/bfs/live/d1ba5f4c54332a21ed2ca0dcecaedd2add587839.png
82 = [耶],http://i0.hdslb.com/bfs/live/eb2d84ba623e2335a48f73fb5bef87bcf53c1239.png
# 用于服务发现返回的后端端点
[registered_endpoints]
# 1 = https://api1.blive.chat
# 允许跨域的源,正则表达式
[cors_origins]
# 1 = https://(?:|.+\.)blive\.chat