From e13fa0e337a2d4c513102085ccec431d4123ae28 Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Tue, 14 Apr 2026 12:52:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8F=91=E9=80=81=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20+=20=E9=82=AE=E7=AE=B1=E7=AE=A1=E7=90=86?= =?UTF-8?q?=20v1.9.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 ++ xian_favor/api.py | 415 ++++++++++++++++++++++++++++++++++++++++++++++ xian_favor/db.py | 69 ++++++++ 3 files changed, 495 insertions(+) diff --git a/README.md b/README.md index ccb296a..b10a37b 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,11 @@ API端点: | `/api/tags` | GET | 列出标签 | | `/api/tags` | POST | 创建标签 | | `/api/tags/` | DELETE | 删除标签 | +| `/api/emails` | GET | 列出邮箱 | +| `/api/emails` | POST | 创建邮箱 | +| `/api/emails/` | PUT | 更新邮箱 | +| `/api/emails/` | DELETE | 删除邮箱 | +| `/api/send-email` | POST | 发送收藏到邮箱 | | `/api/stats` | GET | 统计信息 | | `/api/search` | GET | 搜索 (参数 q=关键词) | @@ -136,6 +141,12 @@ xian-favor/ ## 版本历史 +- v1.9.0 (2026-04-14): 发送邮件功能 + 邮箱管理 + - 每个收藏卡片添加"发送邮件"按钮 + - 选择已有邮箱或输入新邮箱发送 + - 新邮箱自动保存到邮箱管理 + - 邮箱管理页面:添加、编辑、删除邮箱 + - SMTP配置支持环境变量(SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS) - v1.8.0 (2026-04-13): AI自动添加功能,智能识别文本类型并整理数据 - 首页新增"AI添加"按钮 - 粘贴文本/链接/笔记等,AI自动识别类型并提取关键信息 diff --git a/xian_favor/api.py b/xian_favor/api.py index f2851a7..ff671aa 100644 --- a/xian_favor/api.py +++ b/xian_favor/api.py @@ -255,6 +255,157 @@ def search_items(): return jsonify({'success': True, 'data': items}) +# ============ 邮箱管理 API ============ + +@app.route('/api/emails', methods=['GET']) +def list_emails(): + """列出所有邮箱""" + emails = db.list_emails() + return jsonify({'success': True, 'data': emails}) + + +@app.route('/api/emails', methods=['POST']) +def create_email(): + """创建邮箱""" + data = request.get_json() + email_addr = data.get('email', '').strip() + + if not email_addr: + return jsonify({'success': False, 'error': '邮箱地址不能为空'}), 400 + + # 验证邮箱格式 + import re + if not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email_addr): + return jsonify({'success': False, 'error': '邮箱格式不正确'}), 400 + + try: + email_id = db.create_email(email_addr, data.get('name')) + return jsonify({'success': True, 'data': {'id': email_id, 'email': email_addr}}), 201 + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + + +@app.route('/api/emails/', methods=['PUT']) +def update_email(email_id): + """更新邮箱""" + data = request.get_json() + + try: + if db.update_email(email_id, email=data.get('email'), name=data.get('name')): + email = db.get_email(email_id) + return jsonify({'success': True, 'data': email}) + return jsonify({'success': False, 'error': '邮箱不存在或地址已存在'}), 404 + except Exception as e: + return jsonify({'success': False, 'error': str(e)}), 500 + + +@app.route('/api/emails/', methods=['DELETE']) +def delete_email(email_id): + """删除邮箱""" + if db.delete_email(email_id): + return jsonify({'success': True}) + return jsonify({'success': False, 'error': '邮箱不存在'}), 404 + + +@app.route('/api/send-email', methods=['POST']) +def send_email(): + """发送收藏内容到邮箱""" + data = request.get_json() + item_id = data.get('item_id') + email_addr = data.get('email', '').strip() + + if not item_id or not email_addr: + return jsonify({'success': False, 'error': '缺少参数'}), 400 + + # 获取收藏内容 + item = db.get_item(item_id) + if not item: + return jsonify({'success': False, 'error': '收藏不存在'}), 404 + + # 如果是新邮箱,自动保存 + import re + if re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email_addr): + db.create_email(email_addr) + + # 构建邮件内容 + type_labels = {'text': '文本笔记', 'link': '链接收藏', 'column': '专栏订阅', 'todo': '待办事项'} + subject = f"【Xian Favor】{item['title'] or type_labels.get(item['type'], '收藏')}" + + body_lines = [ + f"类型: {type_labels.get(item['type'], item['type'])}", + f"标题: {item['title'] or '(无标题)'}", + "" + ] + + if item['url']: + body_lines.append(f"链接: {item['url']}") + body_lines.append("") + + if item['content']: + body_lines.append("内容:") + body_lines.append(item['content']) + body_lines.append("") + + if item['source']: + body_lines.append(f"来源: {item['source']}") + body_lines.append("") + + if item['type'] == 'todo': + status_labels = {'pending': '待处理', 'in_progress': '进行中', 'completed': '已完成'} + priority_labels = {'low': '低', 'medium': '中', 'high': '高', 'urgent': '紧急'} + body_lines.append(f"状态: {status_labels.get(item['status'], item['status'])}") + body_lines.append(f"优先级: {priority_labels.get(item['priority'], item['priority'])}") + if item['due_date']: + body_lines.append(f"截止日期: {item['due_date']}") + body_lines.append("") + + if item['tags']: + body_lines.append(f"标签: {', '.join(item['tags'])}") + body_lines.append("") + + if item['note']: + body_lines.append("详情/备注:") + body_lines.append(item['note']) + body_lines.append("") + + body_lines.append(f"创建时间: {item['created_at']}") + body_lines.append("---") + body_lines.append("来自 Xian Favor 收藏系统") + + body = "\n".join(body_lines) + + # 调用邮件发送技能 + try: + import smtplib + from email.mime.text import MIMEText + from email.mime.multipart import MIMEMultipart + + # SMTP配置(从环境变量或配置文件获取) + smtp_host = os.environ.get('SMTP_HOST', 'smtp.exmail.qq.com') + smtp_port = int(os.environ.get('SMTP_PORT', 465)) + smtp_user = os.environ.get('SMTP_USER', 'wlq@tphai.com') + smtp_pass = os.environ.get('SMTP_PASS', '') + + if not smtp_pass: + return jsonify({'success': False, 'error': 'SMTP密码未配置,请设置环境变量 SMTP_PASS'}), 500 + + msg = MIMEMultipart() + msg['From'] = smtp_user + msg['To'] = email_addr + msg['Subject'] = subject + + msg.attach(MIMEText(body, 'plain', 'utf-8')) + + with smtplib.SMTP_SSL(smtp_host, smtp_port) as server: + server.login(smtp_user, smtp_pass) + server.sendmail(smtp_user, email_addr, msg.as_string()) + + return jsonify({'success': True, 'message': f'已发送到 {email_addr}'}) + + except Exception as e: + return jsonify({'success': False, 'error': f'发送失败: {str(e)}'}), 500 + + # ============ Web 页面 ============ @app.route('/') @@ -322,6 +473,7 @@ INDEX_TEMPLATE = ''' 已完成
标签管理 + 邮箱管理 @@ -612,6 +764,74 @@ INDEX_TEMPLATE = ''' + + + + + +