fix: resolve 18-point audit items including http payload error helpers, python-dotenv auto-load, and exception wrapping

This commit is contained in:
juanjuanjuanidea
2026-07-24 14:04:39 +08:00
parent e5e1ef3a2d
commit 0e521dc931
5 changed files with 69 additions and 49 deletions
+14 -4
View File
@@ -1,16 +1,22 @@
__version__ = "1.1.0"
import os import os
from flask import Flask, render_template, jsonify from flask import Flask, render_template, jsonify
from dotenv import load_dotenv
from app.db import db from app.db import db
from app.api import accounts, emails from app.api import accounts, emails
__version__ = "1.1.0"
def create_app(): def create_app():
load_dotenv()
app = Flask(__name__) app = Flask(__name__)
db_path = os.getenv("DATABASE_URL", f"sqlite:///{os.path.join(os.getcwd(), 'ni_mail.db')}") data_dir = os.getenv("DATA_DIR", os.getcwd())
default_db = f"sqlite:///{os.path.join(data_dir, 'ni_mail.db')}"
db_path = os.getenv("DATABASE_URL", default_db)
app.config["SQLALCHEMY_DATABASE_URI"] = db_path app.config["SQLALCHEMY_DATABASE_URI"] = db_path
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", "ni-mail-secret-key") app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", "ni-mail-secret-key-v1.1.0")
db.init_app(app) db.init_app(app)
@@ -23,7 +29,11 @@ def create_app():
@app.route("/health") @app.route("/health")
def health(): def health():
return jsonify({"status": "ok", "app": "ni-mail"}) return jsonify({
"status": "ok",
"app": "ni-mail",
"version": __version__
})
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
+40 -36
View File
@@ -18,22 +18,27 @@ def get_emails():
proxy_url = os.getenv("PROXY_URL") proxy_url = os.getenv("PROXY_URL")
messages = [] messages = []
if acct.method == "graph": try:
token_res = graph.get_access_token_graph_result( if acct.method == "graph":
client_id=acct.client_id, token_res = graph.get_access_token_graph_result(
refresh_token=acct.refresh_token, client_id=acct.client_id,
proxy_url=proxy_url refresh_token=acct.refresh_token,
) proxy_url=proxy_url
if isinstance(token_res, dict) and "access_token" in token_res: )
token = token_res["access_token"] if isinstance(token_res, dict) and "access_token" in token_res:
messages = graph.fetch_messages(token, top=15, proxy_url=proxy_url) token = token_res["access_token"]
else: messages = graph.fetch_messages(token, top=15, proxy_url=proxy_url)
messages = imap.fetch_inbox_messages( elif isinstance(token_res, dict) and "error" in token_res:
email=acct.email, return jsonify({"error": token_res["error"]}), 400
password=acct.password or acct.refresh_token, else:
limit=15, messages = imap.fetch_inbox_messages(
proxy_url=proxy_url email=acct.email,
) password=acct.password or acct.refresh_token,
limit=15,
proxy_url=proxy_url
)
except Exception as e:
return jsonify({"error": f"Failed to fetch emails: {str(e)}"}), 500
results = [] results = []
for msg in messages: for msg in messages:
@@ -42,7 +47,7 @@ def get_emails():
results.append({ results.append({
"id": msg.get("id"), "id": msg.get("id"),
"subject": msg.get("subject"), "subject": msg.get("subject"),
"from": msg.get("from", {}).get("emailAddress", {}).get("address", ""), "from": msg.get("from", {}).get("emailAddress", {}).get("address", "") if isinstance(msg.get("from"), dict) else str(msg.get("from", "")),
"received_at": msg.get("receivedDateTime"), "received_at": msg.get("receivedDateTime"),
"otp_code": code, "otp_code": code,
"link": link, "link": link,
@@ -68,21 +73,21 @@ def send_email():
proxy_url = os.getenv("PROXY_URL") proxy_url = os.getenv("PROXY_URL")
if acct.method == "graph": try:
token_res = graph.get_access_token_graph_result( if acct.method == "graph":
client_id=acct.client_id, token_res = graph.get_access_token_graph_result(
refresh_token=acct.refresh_token, client_id=acct.client_id,
proxy_url=proxy_url refresh_token=acct.refresh_token,
) proxy_url=proxy_url
if isinstance(token_res, dict) and "access_token" in token_res: )
token = token_res["access_token"] if isinstance(token_res, dict) and "access_token" in token_res:
success = graph.send_mail_graph(token, to_email, subject, body, proxy_url=proxy_url) token = token_res["access_token"]
if success: success = graph.send_mail_graph(token, to_email, subject, body, proxy_url=proxy_url)
return jsonify({"success": True, "message": "Email sent via Graph API"}) if success:
return jsonify({"error": "Graph API send mail failed"}), 500 return jsonify({"success": True, "message": "Email sent via Graph API"})
return jsonify({"error": "Token refresh failed"}), 400 return jsonify({"error": "Graph API send mail failed"}), 500
else: return jsonify({"error": "Token refresh failed"}), 400
try: else:
success = smtp.send_mail_smtp( success = smtp.send_mail_smtp(
host=os.getenv("SMTP_HOST", "smtp.office365.com"), host=os.getenv("SMTP_HOST", "smtp.office365.com"),
port=int(os.getenv("SMTP_PORT", "587")), port=int(os.getenv("SMTP_PORT", "587")),
@@ -95,7 +100,6 @@ def send_email():
) )
if success: if success:
return jsonify({"success": True, "message": "Email sent via SMTP"}) return jsonify({"success": True, "message": "Email sent via SMTP"})
except Exception as e: return jsonify({"error": "SMTP send mail failed"}), 500
return jsonify({"error": f"SMTP send mail failed: {str(e)}"}), 500 except Exception as e:
return jsonify({"error": f"Send mail failed: {str(e)}"}), 500
return jsonify({"error": "Send mail failed"}), 500
+1
View File
@@ -1,4 +1,5 @@
from __future__ import annotations from __future__ import annotations
from app.services.http import build_error_payload, get_response_details
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
+13 -9
View File
@@ -1,12 +1,16 @@
from __future__ import annotations from typing import Any, Dict
from typing import Any def get_response_details(res) -> str:
import requests
def get_response_details(response: requests.Response) -> Any:
try: try:
return response.json() return res.text[:500]
except Exception: except Exception:
return response.text or response.reason return "No response details"
def build_error_payload(code: str, message: str, error_type: str = "APIError", status_code: int = 500, details: Any = None) -> Dict[str, Any]:
return {
"code": code,
"message": message,
"type": error_type,
"status": status_code,
"details": details
}
+1
View File
@@ -1,4 +1,5 @@
from __future__ import annotations from __future__ import annotations
from app.services.http import build_error_payload, get_response_details
import email import email
import hashlib import hashlib