Merge branch 'brokermr810:main' into main

This commit is contained in:
PengfaGuo
2026-01-17 20:29:37 +08:00
committed by GitHub
35 changed files with 1232 additions and 24324 deletions
+9 -1
View File
@@ -354,8 +354,16 @@ def summary():
""",
(user_id,)
)
recent_trades = cur.fetchall() or []
recent_trades_raw = cur.fetchall() or []
cur.close()
# Convert datetime to timestamp for frontend compatibility
recent_trades = []
for t in recent_trades_raw:
trade = dict(t)
if trade.get('created_at') and hasattr(trade['created_at'], 'timestamp'):
trade['created_at'] = int(trade['created_at'].timestamp())
recent_trades.append(trade)
# Compute performance statistics
perf_stats = _compute_performance_stats(recent_trades)
+15 -70
View File
@@ -228,22 +228,6 @@ CONFIG_SCHEMA = {
'default': '10',
'description': 'Time-to-live for cached price data in seconds'
},
{
'key': 'MARKET_TYPES_JSON',
'label': 'Market Types (JSON)',
'type': 'text',
'default': '[]',
'required': False,
'description': 'Custom market type definitions in JSON format'
},
{
'key': 'TRADING_SUPPORTED_SYMBOLS_JSON',
'label': 'Supported Symbols (JSON)',
'type': 'text',
'default': '[]',
'required': False,
'description': 'List of supported trading symbols in JSON format'
},
]
},
@@ -353,50 +337,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 7. 通知推送 ====================
'notification': {
'title': 'Notifications',
'icon': 'notification',
'order': 7,
'items': [
{
'key': 'SIGNAL_WEBHOOK_URL',
'label': 'Webhook URL',
'type': 'text',
'required': False,
'description': 'Custom webhook URL for signal notifications (POST JSON)'
},
{
'key': 'SIGNAL_WEBHOOK_TOKEN',
'label': 'Webhook Token',
'type': 'password',
'required': False,
'description': 'Authentication token sent in webhook header'
},
{
'key': 'SIGNAL_NOTIFY_TIMEOUT_SEC',
'label': 'Notify Timeout (sec)',
'type': 'number',
'default': '6',
'description': 'Notification request timeout'
},
{
'key': 'TELEGRAM_BOT_TOKEN',
'label': 'Telegram Bot Token',
'type': 'password',
'required': False,
'link': 'https://t.me/BotFather',
'link_text': 'settings.link.createBot',
'description': 'Telegram bot token from @BotFather for signal notifications'
},
]
},
# ==================== 8. 邮件配置 ====================
# ==================== 7. 邮件配置 (公共 SMTP) ====================
'email': {
'title': 'Email (SMTP)',
'icon': 'mail',
'order': 8,
'order': 7,
'items': [
{
'key': 'SMTP_HOST',
@@ -454,7 +399,7 @@ CONFIG_SCHEMA = {
'sms': {
'title': 'SMS (Twilio)',
'icon': 'phone',
'order': 9,
'order': 8,
'items': [
{
'key': 'TWILIO_ACCOUNT_SID',
@@ -482,11 +427,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 10. AI Agent 配置 ====================
# ==================== 9. AI Agent 配置 ====================
'agent': {
'title': 'AI Agent',
'icon': 'experiment',
'order': 10,
'order': 9,
'items': [
{
'key': 'ENABLE_AGENT_MEMORY',
@@ -568,11 +513,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 11. 网络代理 ====================
# ==================== 10. 网络代理 ====================
'network': {
'title': 'Network & Proxy',
'icon': 'global',
'order': 11,
'order': 10,
'items': [
{
'key': 'PROXY_HOST',
@@ -606,11 +551,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 12. 搜索配置 ====================
# ==================== 11. 搜索配置 ====================
'search': {
'title': 'Web Search',
'icon': 'search',
'order': 12,
'order': 11,
'items': [
{
'key': 'SEARCH_PROVIDER',
@@ -664,11 +609,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 13. 注册与安全 ====================
# ==================== 12. 注册与安全 ====================
'security': {
'title': 'Registration & Security',
'icon': 'safety',
'order': 13,
'order': 12,
'items': [
{
'key': 'ENABLE_REGISTRATION',
@@ -826,11 +771,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 14. 计费配置 ====================
# ==================== 13. 计费配置 ====================
'billing': {
'title': 'Billing & Credits',
'icon': 'dollar',
'order': 14,
'order': 13,
'items': [
{
'key': 'BILLING_ENABLED',
@@ -898,11 +843,11 @@ CONFIG_SCHEMA = {
]
},
# ==================== 15. 应用配置 ====================
# ==================== 14. 应用配置 ====================
'app': {
'title': 'Application',
'icon': 'appstore',
'order': 15,
'order': 14,
'items': [
{
'key': 'CORS_ORIGINS',
+7 -1
View File
@@ -471,7 +471,13 @@ def get_equity_curve():
equity += float(r.get('profit') or 0)
except Exception:
pass
ts = int(r.get('created_at').timestamp() or time.time())
created_at = r.get('created_at')
if created_at and hasattr(created_at, 'timestamp'):
ts = int(created_at.timestamp())
elif created_at:
ts = int(created_at)
else:
ts = int(time.time())
curve.append({'time': ts, 'equity': equity})
return jsonify({'code': 1, 'msg': 'success', 'data': curve})
+147 -1
View File
@@ -348,9 +348,11 @@ def get_user_credits_log():
@user_bp.route('/profile', methods=['GET'])
@login_required
def get_profile():
"""Get current user's profile with billing info"""
"""Get current user's profile with billing info and notification settings"""
try:
import json
from app.services.billing_service import get_billing_service
from app.utils.db import get_db_connection
user_id = getattr(g, 'user_id', None)
if not user_id:
@@ -367,6 +369,27 @@ def get_profile():
billing_info = get_billing_service().get_user_billing_info(user_id)
user['billing'] = billing_info
# Add notification settings
with get_db_connection() as db:
cur = db.cursor()
cur.execute("SELECT notification_settings FROM qd_users WHERE id = ?", (user_id,))
row = cur.fetchone()
cur.close()
settings_str = (row.get('notification_settings') if row else '') or ''
notification_settings = {}
if settings_str:
try:
notification_settings = json.loads(settings_str)
except Exception:
notification_settings = {}
# Default values
if 'default_channels' not in notification_settings:
notification_settings['default_channels'] = ['browser']
user['notification_settings'] = notification_settings
return jsonify({
'code': 1,
'msg': 'success',
@@ -529,6 +552,129 @@ def get_my_referrals():
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@user_bp.route('/notification-settings', methods=['GET'])
@login_required
def get_notification_settings():
"""
Get current user's notification settings.
Returns:
notification_settings: {
default_channels: ['browser', 'telegram', ...],
telegram_chat_id: str,
email: str (optional, override for notifications),
discord_webhook: str (optional)
}
"""
try:
import json
from app.utils.db import get_db_connection
user_id = getattr(g, 'user_id', None)
if not user_id:
return jsonify({'code': 0, 'msg': 'Not authenticated', 'data': None}), 401
with get_db_connection() as db:
cur = db.cursor()
cur.execute("SELECT notification_settings, email FROM qd_users WHERE id = ?", (user_id,))
row = cur.fetchone()
cur.close()
if not row:
return jsonify({'code': 0, 'msg': 'User not found', 'data': None}), 404
# Parse notification_settings JSON
settings_str = row.get('notification_settings') or ''
settings = {}
if settings_str:
try:
settings = json.loads(settings_str)
except Exception:
settings = {}
# Default values
if 'default_channels' not in settings:
settings['default_channels'] = ['browser']
if 'email' not in settings:
settings['email'] = row.get('email') or ''
return jsonify({
'code': 1,
'msg': 'success',
'data': settings
})
except Exception as e:
logger.error(f"get_notification_settings failed: {e}")
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@user_bp.route('/notification-settings', methods=['PUT'])
@login_required
def update_notification_settings():
"""
Update current user's notification settings.
Request body:
default_channels: list of str (optional, e.g. ['browser', 'telegram'])
telegram_bot_token: str (optional, user's own Telegram bot token)
telegram_chat_id: str (optional)
email: str (optional, for notification override)
discord_webhook: str (optional)
"""
try:
import json
from app.utils.db import get_db_connection
user_id = getattr(g, 'user_id', None)
if not user_id:
return jsonify({'code': 0, 'msg': 'Not authenticated', 'data': None}), 401
data = request.get_json() or {}
# Validate channels
valid_channels = ['browser', 'email', 'telegram', 'discord', 'webhook', 'phone']
default_channels = data.get('default_channels', [])
if not isinstance(default_channels, list):
default_channels = ['browser']
default_channels = [c for c in default_channels if c in valid_channels]
if not default_channels:
default_channels = ['browser']
# Build settings object
settings = {
'default_channels': default_channels,
'telegram_bot_token': str(data.get('telegram_bot_token') or '').strip(),
'telegram_chat_id': str(data.get('telegram_chat_id') or '').strip(),
'email': str(data.get('email') or '').strip(),
'discord_webhook': str(data.get('discord_webhook') or '').strip(),
'webhook_url': str(data.get('webhook_url') or '').strip(),
'phone': str(data.get('phone') or '').strip(),
}
# Remove empty values (but keep default_channels and telegram_bot_token even if partially filled)
settings = {k: v for k, v in settings.items() if v or k == 'default_channels'}
settings_json = json.dumps(settings, ensure_ascii=False)
with get_db_connection() as db:
cur = db.cursor()
cur.execute(
"UPDATE qd_users SET notification_settings = ?, updated_at = NOW() WHERE id = ?",
(settings_json, user_id)
)
db.commit()
cur.close()
return jsonify({
'code': 1,
'msg': 'Notification settings updated',
'data': settings
})
except Exception as e:
logger.error(f"update_notification_settings failed: {e}")
return jsonify({'code': 0, 'msg': str(e), 'data': None}), 500
@user_bp.route('/change-password', methods=['POST'])
@login_required
def change_password():