完善开源交付文档与Docker部署安全配置
This commit is contained in:
@@ -4,13 +4,18 @@ port = 8888
|
||||
|
||||
[auth]
|
||||
username = "admin"
|
||||
password = "admin123"
|
||||
# 公开仓库不再提供弱默认密码:
|
||||
# 1. 新部署环境首次启动时会自动把这里的账号信息播种到后台管理员表;
|
||||
# 2. 若你的数据库里已经存在管理员账号,则后续以数据库中的真实密码为准;
|
||||
# 3. 正式环境建议继续通过环境变量覆盖,而不是长期使用仓库示例值。
|
||||
password = "ChangeThisPassword_2026!"
|
||||
|
||||
[trendradar_webhook]
|
||||
# 是否启用 TrendRadar webhook 适配接口
|
||||
enabled = true
|
||||
# 固定 token(建议配置),支持请求头 X-Webhook-Token / query token / payload.token 三种传法
|
||||
token = "watHcBbQIxtmyqGRSHKeTDRVjkHOceiRfFytUkQUwmV"
|
||||
enabled = false
|
||||
# 固定 token(建议配置),支持请求头 X-Webhook-Token / query token / payload.token 三种传法。
|
||||
# 仓库中的值仅为占位示例,正式环境请改成你自己的随机长 token。
|
||||
token = "please_change_this_token"
|
||||
# 默认推送目标群(可配置多个)
|
||||
default_group_ids = []
|
||||
# 是否允许 payload 覆盖目标群(开启后可通过 target_group_ids/group_id 指定)
|
||||
|
||||
@@ -153,23 +153,146 @@ class DashboardServer:
|
||||
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.toml')
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return toml.load(f)
|
||||
config_data = toml.load(f)
|
||||
else:
|
||||
# 如果配置文件不存在,创建默认配置
|
||||
default_config = {
|
||||
config_data = {
|
||||
"server": {"host": "0.0.0.0", "port": 8888},
|
||||
"auth": {"username": "admin", "password": "admin123"}
|
||||
"auth": {"username": "admin", "password": "ChangeThisPassword_2026!"},
|
||||
"trendradar_webhook": {
|
||||
"enabled": False,
|
||||
"token": "please_change_this_token",
|
||||
"default_group_ids": [],
|
||||
"allow_payload_target_groups": False,
|
||||
"send_timeout_seconds": 20,
|
||||
},
|
||||
}
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
toml.dump(default_config, f)
|
||||
return default_config
|
||||
toml.dump(config_data, f)
|
||||
return self._apply_dashboard_env_overrides(config_data)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"加载Dashboard配置文件失败: {e}")
|
||||
# 这里属于 Dashboard 构造早期阶段:
|
||||
# 1. `self.LOG` 还没在 `__init__` 中完成赋值;
|
||||
# 2. 若此时配置文件损坏,仍然需要把错误稳定打印出来;
|
||||
# 3. 因此这里直接回退到模块级 logger,避免异常再次被属性缺失覆盖。
|
||||
logger.error(f"加载Dashboard配置文件失败: {e}")
|
||||
# 返回默认配置
|
||||
return {
|
||||
fallback_config = {
|
||||
"server": {"host": "0.0.0.0", "port": 8888},
|
||||
"auth": {"username": "admin", "password": "admin123"}
|
||||
"auth": {"username": "admin", "password": "ChangeThisPassword_2026!"},
|
||||
"trendradar_webhook": {
|
||||
"enabled": False,
|
||||
"token": "please_change_this_token",
|
||||
"default_group_ids": [],
|
||||
"allow_payload_target_groups": False,
|
||||
"send_timeout_seconds": 20,
|
||||
},
|
||||
}
|
||||
return self._apply_dashboard_env_overrides(fallback_config)
|
||||
|
||||
@staticmethod
|
||||
def _safe_env_bool(value, default: bool) -> bool:
|
||||
"""把环境变量里的开关值安全转成布尔值。"""
|
||||
if value in (None, ""):
|
||||
return default
|
||||
text = str(value).strip().lower()
|
||||
if text in {"1", "true", "yes", "y", "on"}:
|
||||
return True
|
||||
if text in {"0", "false", "no", "n", "off"}:
|
||||
return False
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def _safe_env_int(value, default: int) -> int:
|
||||
"""把环境变量里的数字安全转成整数。"""
|
||||
try:
|
||||
if value in (None, ""):
|
||||
return default
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def _safe_env_group_ids(value) -> list[str]:
|
||||
"""把逗号分隔的群列表环境变量转成数组。"""
|
||||
raw_text = str(value or "").strip()
|
||||
if not raw_text:
|
||||
return []
|
||||
return [item.strip() for item in raw_text.split(",") if item.strip()]
|
||||
|
||||
def _apply_dashboard_env_overrides(self, config_data: dict) -> dict:
|
||||
"""用环境变量覆盖 Dashboard 配置中的敏感项与部署项。"""
|
||||
merged_config = dict(config_data or {})
|
||||
merged_config["server"] = dict(merged_config.get("server", {}) or {})
|
||||
merged_config["auth"] = dict(merged_config.get("auth", {}) or {})
|
||||
merged_config["trendradar_webhook"] = dict(merged_config.get("trendradar_webhook", {}) or {})
|
||||
|
||||
# 这里优先让运维通过环境变量覆盖后台配置:
|
||||
# 1. Docker / PaaS 场景下更容易统一管理账号、端口与 token;
|
||||
# 2. 也避免公开仓库里的示例 TOML 被误当成最终生产配置;
|
||||
# 3. 未配置环境变量时仍回退到本地文件,兼容现有非容器部署方式。
|
||||
server_section = merged_config["server"]
|
||||
auth_section = merged_config["auth"]
|
||||
webhook_section = merged_config["trendradar_webhook"]
|
||||
|
||||
server_section["host"] = str(os.environ.get("DASHBOARD_HOST", server_section.get("host", "0.0.0.0")) or "0.0.0.0")
|
||||
server_section["port"] = self._safe_env_int(
|
||||
os.environ.get("DASHBOARD_PORT", server_section.get("port", 8888)),
|
||||
8888,
|
||||
)
|
||||
|
||||
auth_section["username"] = str(
|
||||
os.environ.get("ABOT_DASHBOARD_USERNAME", auth_section.get("username", "admin")) or "admin"
|
||||
).strip()
|
||||
auth_section["password"] = str(
|
||||
os.environ.get("ABOT_DASHBOARD_PASSWORD", auth_section.get("password", "ChangeThisPassword_2026!"))
|
||||
or "ChangeThisPassword_2026!"
|
||||
)
|
||||
auth_section["session_timeout_minutes"] = self._safe_env_int(
|
||||
os.environ.get("ABOT_DASHBOARD_SESSION_TIMEOUT_MINUTES", auth_section.get("session_timeout_minutes", 480)),
|
||||
480,
|
||||
)
|
||||
auth_section["max_failed_attempts"] = self._safe_env_int(
|
||||
os.environ.get("ABOT_DASHBOARD_MAX_FAILED_ATTEMPTS", auth_section.get("max_failed_attempts", 5)),
|
||||
5,
|
||||
)
|
||||
auth_section["lock_seconds"] = self._safe_env_int(
|
||||
os.environ.get("ABOT_DASHBOARD_LOCK_SECONDS", auth_section.get("lock_seconds", 900)),
|
||||
900,
|
||||
)
|
||||
auth_section["cookie_secure"] = self._safe_env_bool(
|
||||
os.environ.get("ABOT_DASHBOARD_COOKIE_SECURE", auth_section.get("cookie_secure", False)),
|
||||
False,
|
||||
)
|
||||
auth_section["cookie_samesite"] = str(
|
||||
os.environ.get("ABOT_DASHBOARD_COOKIE_SAMESITE", auth_section.get("cookie_samesite", "Lax")) or "Lax"
|
||||
).strip()
|
||||
|
||||
webhook_section["enabled"] = self._safe_env_bool(
|
||||
os.environ.get("ABOT_TRENDRADAR_WEBHOOK_ENABLED", webhook_section.get("enabled", False)),
|
||||
False,
|
||||
)
|
||||
webhook_section["token"] = str(
|
||||
os.environ.get("ABOT_TRENDRADAR_WEBHOOK_TOKEN", webhook_section.get("token", "")) or ""
|
||||
).strip()
|
||||
env_default_groups = os.environ.get("ABOT_TRENDRADAR_DEFAULT_GROUP_IDS", "")
|
||||
if env_default_groups not in (None, ""):
|
||||
webhook_section["default_group_ids"] = self._safe_env_group_ids(env_default_groups)
|
||||
webhook_section["allow_payload_target_groups"] = self._safe_env_bool(
|
||||
os.environ.get(
|
||||
"ABOT_TRENDRADAR_ALLOW_PAYLOAD_TARGET_GROUPS",
|
||||
webhook_section.get("allow_payload_target_groups", False),
|
||||
),
|
||||
False,
|
||||
)
|
||||
webhook_section["send_timeout_seconds"] = self._safe_env_int(
|
||||
os.environ.get(
|
||||
"ABOT_TRENDRADAR_SEND_TIMEOUT_SECONDS",
|
||||
webhook_section.get("send_timeout_seconds", 20),
|
||||
),
|
||||
20,
|
||||
)
|
||||
return merged_config
|
||||
|
||||
def _create_app(self) -> Flask:
|
||||
"""创建Flask应用"""
|
||||
|
||||
Reference in New Issue
Block a user