# 获取中国银行外汇牌价并插入 exchange_rate 表 # GET /accounting/api/fetch_forex_rates.dspy # # 安全:本端点是定时任务入口(会发起外网抓取 + 写库),只允许本机调用。 # 之前无任何鉴权且授权给 any 角色,匿名可反复触发 → 外部抓取放大 + 脏数据写入 + DoS。 # 判定依据 client_ip:nginx 用 $proxy_add_x_forwarded_for 追加模式、中间件取链尾值, # 外部伪造 X-Forwarded-For 会被追加真实 IP 到链尾,伪造不成立;且应用端口不对外开放。 _ip = request.get('client_ip') or '' if _ip not in ('127.0.0.1', '::1', 'localhost'): return json.dumps({'success': False, 'message': '仅允许本机调用(定时任务入口)'}, ensure_ascii=False) import re import urllib.request from datetime import date BOC_URL = 'https://www.boc.cn/sourcedb/whpj/index.html' CURRENCIES = {'美元': 'USD', '日元': 'JPY', '英镑': 'GBP'} async with get_sor_context(request._run_ns, 'accounting') as sor: # 1. 抓取 BOC 页面 try: req = urllib.request.Request(BOC_URL, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=15) as resp: html = resp.read().decode('utf-8', errors='replace') except Exception as e: return json.dumps({'success': False, 'message': f'获取BOC页面失败: {e}'}, ensure_ascii=False) today = date.today().isoformat() inserted = 0 for cn_name, code in CURRENCIES.items(): pos = html.find(cn_name) if pos < 0: continue chunk = html[pos:pos+500] tds = re.findall(r'