Skip to content

Commit ea04fde

Browse files
committed
Integrate Vite frontend with mailbox operations
1 parent 7fe039c commit ea04fde

32 files changed

Lines changed: 9670 additions & 426 deletions

app.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pathlib import Path
1313
from typing import Any, Callable
1414

15-
from flask import Flask, jsonify, render_template, request, session
15+
from flask import Flask, jsonify, render_template, request, send_from_directory, session
1616

1717
from services.outlook_manager import (
1818
FlagStateUpdateRequest,
@@ -45,6 +45,8 @@ def create_app(
4545
app = Flask(__name__)
4646
app.config["JSON_AS_ASCII"] = False
4747
app.config["SECRET_KEY"] = os.getenv("MAIL_ADMIN_SECRET_KEY", "change-me-before-production")
48+
frontend_dist_dir = Path(app.static_folder or "static") / "frontend"
49+
frontend_index_file = frontend_dist_dir / "index.html"
4850

4951
mailbox_manager = manager or MailboxManager()
5052
mailbox_store = store or MailboxStore(database_path or os.getenv("MAILBOX_DB_PATH", "data/mailboxes.db"))
@@ -76,12 +78,21 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
7678

7779
@app.get("/")
7880
def index() -> str:
79-
return render_template("index.html")
81+
return _serve_frontend_index(frontend_index_file, frontend_dist_dir)
8082

8183
@app.get("/favicon.ico")
8284
def favicon() -> tuple[str, int]:
8385
return "", 204
8486

87+
@app.get("/<path:path>")
88+
def frontend_routes(path: str) -> Any:
89+
normalized_path = path.strip()
90+
if normalized_path.startswith("api/") or normalized_path == "api":
91+
raise MailboxError("接口不存在", code="not_found", status_code=404)
92+
if normalized_path.startswith("static/"):
93+
raise MailboxError("静态资源不存在", code="not_found", status_code=404)
94+
return _serve_frontend_index(frontend_index_file, frontend_dist_dir)
95+
8596
@app.get("/api/health")
8697
def health() -> Any:
8798
return jsonify({"status": "ok", "service": "inboxops"})
@@ -2310,6 +2321,34 @@ def _probe_mailbox_connection(
23102321
return "连接成功,收件箱暂无邮件" if not latest_subject else f"连接成功,最近邮件:{latest_subject}"
23112322

23122323

2324+
def _serve_frontend_index(frontend_index_file: Path, frontend_dist_dir: Path) -> Any:
2325+
if frontend_index_file.exists():
2326+
return send_from_directory(frontend_dist_dir, "index.html")
2327+
return (
2328+
"""
2329+
<!doctype html>
2330+
<html lang="zh-CN">
2331+
<head>
2332+
<meta charset="utf-8" />
2333+
<meta name="viewport" content="width=device-width, initial-scale=1" />
2334+
<title>Email Outlook</title>
2335+
<style>
2336+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; padding: 40px; color: #0f172a; }
2337+
code { background: #f1f5f9; padding: 2px 6px; border-radius: 6px; }
2338+
</style>
2339+
</head>
2340+
<body>
2341+
<h1>前端尚未构建</h1>
2342+
<p>请在 <code>templates</code> 目录执行 <code>npm install</code> 与 <code>npm run build</code>。</p>
2343+
<p>开发模式可执行 <code>npm run dev</code>,并通过 Vite 本地地址访问。</p>
2344+
</body>
2345+
</html>
2346+
""",
2347+
503,
2348+
{"Content-Type": "text/html; charset=utf-8"},
2349+
)
2350+
2351+
23132352
def _label_for_method(method: str) -> str:
23142353
return {
23152354
"graph_api": "Graph API",

static/frontend/assets/index-BX5hM8t0.js

Lines changed: 254 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/frontend/assets/index-DpDwmoZV.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/frontend/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Email Outlook</title>
7+
<script type="module" crossorigin src="/static/frontend/assets/index-BX5hM8t0.js"></script>
8+
<link rel="stylesheet" crossorigin href="/static/frontend/assets/index-DpDwmoZV.css">
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
</body>
13+
</html>

templates/.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# GEMINI_API_KEY: Required for Gemini AI API calls.
2+
# AI Studio automatically injects this at runtime from user secrets.
3+
# Users configure this via the Secrets panel in the AI Studio UI.
4+
GEMINI_API_KEY="MY_GEMINI_API_KEY"
5+
6+
# APP_URL: The URL where this applet is hosted.
7+
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
8+
# Used for self-referential links, OAuth callbacks, and API endpoints.
9+
APP_URL="MY_APP_URL"

templates/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules/
2+
build/
3+
dist/
4+
coverage/
5+
.DS_Store
6+
*.log
7+
.env*
8+
!.env.example

templates/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div align="center">
2+
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
3+
</div>
4+
5+
# Run and deploy your AI Studio app
6+
7+
This contains everything you need to run your app locally.
8+
9+
View your app in AI Studio: https://ai.studio/apps/ad6a629e-1054-4fa3-b887-217170191b31
10+
11+
## Run Locally
12+
13+
**Prerequisites:** Node.js
14+
15+
16+
1. Install dependencies:
17+
`npm install`
18+
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
19+
3. Run the app:
20+
`npm run dev`

0 commit comments

Comments
 (0)