mirror of
https://github.com/xfgryujk/blivechat.git
synced 2026-08-22 11:23:28 +08:00
添加服务发现接口、支持跨域
This commit is contained in:
21
api/base.py
21
api/base.py
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
# 通配的放在最后
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
51
config.py
51
config.py
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user