Skip to content
This repository was archived by the owner on Apr 22, 2026. It is now read-only.

Commit 653dcdb

Browse files
authored
Merge pull request #5 from code-better-life/feat/cf-email-routing
feat: 新增 Cloudflare Email Routing + Gmail API 邮箱提供商
2 parents fd7ecaa + 02460cd commit 653dcdb

12 files changed

Lines changed: 876 additions & 0 deletions

.dockerignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.venv
2+
__pycache__
3+
*.pyc
4+
.git
5+
.DS_Store
6+
data/
7+
*.db
8+
gui_config.json
9+
client_secret.json
10+
accounts*.json
11+
accounts*.txt
12+
*.log
13+
.idea
14+
.cursor
15+
build/
16+
dist/
17+
release-artifacts/
18+
uv.lock
19+
pyproject.toml

Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM python:3.13-slim
2+
3+
WORKDIR /app
4+
5+
RUN pip install --no-cache-dir requests curl_cffi
6+
7+
COPY codex_register/ codex_register/
8+
COPY gui.py .
9+
COPY main.py .
10+
COPY VERSION .
11+
COPY scripts/ scripts/
12+
13+
ENV HOST=0.0.0.0
14+
ENV PORT=8765
15+
16+
EXPOSE 8765
17+
18+
CMD ["python", "/app/gui.py", "--mode", "browser", "--no-auto-open", "--host", "0.0.0.0", "--port", "8765"]

codex_register/gui_config_store.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@
7070
"gmail_imap_port": 993,
7171
"gmail_alias_tag_len": 8,
7272
"gmail_alias_mix_googlemail": True,
73+
"cf_routing_api_token": "",
74+
"cf_routing_zone_id": "",
75+
"cf_routing_domain": "",
76+
"cf_routing_cleanup": True,
77+
"gmail_api_client_id": "",
78+
"gmail_api_client_secret": "",
79+
"gmail_api_refresh_token": "",
80+
"gmail_api_user": "",
7381
"hero_sms_enabled": False,
7482
"hero_sms_api_key": "",
7583
"hero_sms_service": "",
@@ -180,6 +188,15 @@ def load_config() -> dict[str, Any]:
180188
cfg["flclash_rotate_every"] = int(env.get("FLCLASH_ROTATE_EVERY", "3") or 3)
181189
except Exception:
182190
cfg["flclash_rotate_every"] = 3
191+
cfg["cf_routing_api_token"] = env.get("CF_ROUTING_API_TOKEN", env.get("CF_API_TOKEN", ""))
192+
cfg["cf_routing_zone_id"] = env.get("CF_ROUTING_ZONE_ID", env.get("CF_ZONE_ID", ""))
193+
cfg["cf_routing_domain"] = env.get("CF_ROUTING_DOMAIN", env.get("EMAIL_DOMAIN", ""))
194+
cleanup_v = env.get("CF_ROUTING_CLEANUP", "1").strip().lower()
195+
cfg["cf_routing_cleanup"] = cleanup_v not in ("0", "false", "no")
196+
cfg["gmail_api_client_id"] = env.get("GMAIL_API_CLIENT_ID", env.get("GMAIL_CLIENT_ID", ""))
197+
cfg["gmail_api_client_secret"] = env.get("GMAIL_API_CLIENT_SECRET", env.get("GMAIL_CLIENT_SECRET", ""))
198+
cfg["gmail_api_refresh_token"] = env.get("GMAIL_API_REFRESH_TOKEN", env.get("GMAIL_REFRESH_TOKEN", ""))
199+
cfg["gmail_api_user"] = env.get("GMAIL_API_USER", env.get("GMAIL_USER", ""))
183200
cfg["gmail_imap_user"] = env.get("GMAIL_IMAP_USER", env.get("IMAP_USER", ""))
184201
cfg["gmail_imap_pass"] = env.get("GMAIL_IMAP_PASS", env.get("IMAP_PASS", ""))
185202
cfg["gmail_alias_emails"] = env.get("GMAIL_ALIAS_EMAILS", env.get("EMAIL_LIST", ""))

codex_register/gui_frontend_app_setup.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@
131131
gmail_imap_port: 993,
132132
gmail_alias_tag_len: 8,
133133
gmail_alias_mix_googlemail: true,
134+
cf_routing_api_token: "",
135+
cf_routing_zone_id: "",
136+
cf_routing_domain: "",
137+
cf_routing_cleanup: true,
138+
gmail_api_client_id: "",
139+
gmail_api_client_secret: "",
140+
gmail_api_refresh_token: "",
141+
gmail_api_user: "",
134142
hero_sms_enabled: false,
135143
hero_sms_reuse_phone: false,
136144
hero_sms_api_key: "",
@@ -369,6 +377,13 @@
369377
selectedMailbox: "",
370378
mailRows: [],
371379
mailTotal: 0
380+
},
381+
cf_email_routing: {
382+
domains: [],
383+
mailboxRows: [],
384+
selectedMailbox: "",
385+
mailRows: [],
386+
mailTotal: 0
372387
}
373388
});
374389

@@ -960,6 +975,7 @@
960975
if (["luckyous", "luckyous_api", "luckymail", "lucky_mail", "luckyous_openapi"].includes(val)) return "luckyous";
961976
if (val === "graph") return "graph";
962977
if (val === "gmail" || val === "imap") return "gmail";
978+
if (["cf_email_routing", "cf_routing", "cloudflare_email_routing", "cloudflare_routing"].includes(val)) return "cf_email_routing";
963979
return "mailfree";
964980
}
965981

@@ -1531,6 +1547,14 @@
15311547
settingsForm.gmail_imap_port = Number(cfg.gmail_imap_port || 993);
15321548
settingsForm.gmail_alias_tag_len = Number(cfg.gmail_alias_tag_len || 8);
15331549
settingsForm.gmail_alias_mix_googlemail = cfg.gmail_alias_mix_googlemail !== false;
1550+
settingsForm.cf_routing_api_token = String(cfg.cf_routing_api_token || "");
1551+
settingsForm.cf_routing_zone_id = String(cfg.cf_routing_zone_id || "");
1552+
settingsForm.cf_routing_domain = String(cfg.cf_routing_domain || "");
1553+
settingsForm.cf_routing_cleanup = cfg.cf_routing_cleanup !== false;
1554+
settingsForm.gmail_api_client_id = String(cfg.gmail_api_client_id || "");
1555+
settingsForm.gmail_api_client_secret = String(cfg.gmail_api_client_secret || "");
1556+
settingsForm.gmail_api_refresh_token = String(cfg.gmail_api_refresh_token || "");
1557+
settingsForm.gmail_api_user = String(cfg.gmail_api_user || "");
15341558
settingsForm.hero_sms_enabled = !!cfg.hero_sms_enabled;
15351559
settingsForm.hero_sms_reuse_phone = !!cfg.hero_sms_reuse_phone;
15361560
settingsForm.hero_sms_api_key = String(cfg.hero_sms_api_key || "");
@@ -1621,6 +1645,14 @@
16211645
gmail_imap_port: Number(settingsForm.gmail_imap_port || 993),
16221646
gmail_alias_tag_len: Number(settingsForm.gmail_alias_tag_len || 8),
16231647
gmail_alias_mix_googlemail: !!settingsForm.gmail_alias_mix_googlemail,
1648+
cf_routing_api_token: String(settingsForm.cf_routing_api_token || "").trim(),
1649+
cf_routing_zone_id: String(settingsForm.cf_routing_zone_id || "").trim(),
1650+
cf_routing_domain: String(settingsForm.cf_routing_domain || "").trim(),
1651+
cf_routing_cleanup: !!settingsForm.cf_routing_cleanup,
1652+
gmail_api_client_id: String(settingsForm.gmail_api_client_id || "").trim(),
1653+
gmail_api_client_secret: String(settingsForm.gmail_api_client_secret || "").trim(),
1654+
gmail_api_refresh_token: String(settingsForm.gmail_api_refresh_token || "").trim(),
1655+
gmail_api_user: String(settingsForm.gmail_api_user || "").trim(),
16241656
hero_sms_enabled: !!settingsForm.hero_sms_enabled,
16251657
hero_sms_reuse_phone: !!settingsForm.hero_sms_reuse_phone,
16261658
hero_sms_api_key: String(settingsForm.hero_sms_api_key || "").trim(),

codex_register/gui_frontend_app_template.html

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ <h1>注册任务面板</h1>
378378
<n-tab-pane name="graph" tab="Microsoft Graph">
379379
<span style="display:none"></span>
380380
</n-tab-pane>
381+
<n-tab-pane name="cf_email_routing" tab="CF Email Routing">
382+
<span style="display:none"></span>
383+
</n-tab-pane>
381384
</n-tabs>
382385
<div class="toolbar mail-top-toolbar">
383386
<n-button class="toolbar-btn" :loading="loading.mail_overview || loading.save" @click="refreshMailOverview(true)">保存并刷新</n-button>
@@ -633,6 +636,63 @@ <h1>注册任务面板</h1>
633636
>删除当前文件</n-button>
634637
<n-button class="toolbar-btn" :loading="loading.graph_files" @click="refreshGraphAccountFiles(true)">刷新文件列表</n-button>
635638
</div>
639+
<div v-else-if="mailProviderTab === 'cf_email_routing'" style="margin-top: 10px">
640+
<div class="meta" style="margin-bottom: 8px; font-weight: 500">Cloudflare Email Routing</div>
641+
<div class="toolbar">
642+
<n-input
643+
class="toolbar-path"
644+
v-model:value="settingsForm.cf_routing_api_token"
645+
type="password"
646+
show-password-on="click"
647+
placeholder="Cloudflare API Token(需 Zone:Email Routing:Edit 权限)"
648+
/>
649+
<n-input
650+
class="toolbar-path"
651+
v-model:value="settingsForm.cf_routing_zone_id"
652+
placeholder="Cloudflare Zone ID"
653+
/>
654+
<n-input
655+
class="toolbar-path"
656+
v-model:value="settingsForm.cf_routing_domain"
657+
placeholder="邮箱域名(如 example.com)"
658+
/>
659+
<n-switch v-model:value="settingsForm.cf_routing_cleanup" />
660+
<span class="meta">注册后自动清理规则</span>
661+
</div>
662+
<div class="meta" style="margin-top: 12px; margin-bottom: 8px; font-weight: 500">Gmail API(OAuth2 收件)</div>
663+
<div class="toolbar">
664+
<n-input
665+
class="toolbar-path"
666+
v-model:value="settingsForm.gmail_api_user"
667+
placeholder="Gmail 收件地址(转发目标,如 yourname@gmail.com)"
668+
/>
669+
<n-input
670+
class="toolbar-path"
671+
v-model:value="settingsForm.gmail_api_client_id"
672+
placeholder="Google Cloud OAuth Client ID"
673+
/>
674+
<n-input
675+
class="toolbar-path"
676+
v-model:value="settingsForm.gmail_api_client_secret"
677+
type="password"
678+
show-password-on="click"
679+
placeholder="Google Cloud OAuth Client Secret"
680+
/>
681+
<n-input
682+
class="toolbar-path"
683+
v-model:value="settingsForm.gmail_api_refresh_token"
684+
type="password"
685+
show-password-on="click"
686+
placeholder="Gmail OAuth Refresh Token"
687+
/>
688+
</div>
689+
</div>
690+
<div v-if="mailProviderTab === 'cf_email_routing'" class="meta" style="margin-top: 6px">
691+
通过 Cloudflare Email Routing 动态创建转发规则(<code>reg_xxx@域名</code> → Gmail),使用 Gmail API(OAuth2)轮询收件箱提取验证码。
692+
</div>
693+
<div v-if="mailProviderTab === 'cf_email_routing'" class="meta" style="margin-top: 4px">
694+
Cloudflare Token 权限要求:<code>Zone → Email Routing → Edit</code>。Gmail 需在 Google Cloud Console 创建 OAuth 凭据并获取 Refresh Token。
695+
</div>
636696
<div v-if="mailProviderTab === 'gmail'" class="meta" style="margin-top: 6px">
637697
Gmail 必须开启 IMAP 并使用应用专用密码;支持按别名池自动生成 <code>user+tag@gmail.com</code>
638698
</div>

codex_register/gui_service.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,13 @@ def update_config(self, data: dict[str, Any], emit_log: bool = True) -> dict[str
14921492
"cliproxy_management_key",
14931493
"accounts_list_timezone",
14941494
"codex_export_dir",
1495+
"cf_routing_api_token",
1496+
"cf_routing_zone_id",
1497+
"cf_routing_domain",
1498+
"gmail_api_client_id",
1499+
"gmail_api_client_secret",
1500+
"gmail_api_refresh_token",
1501+
"gmail_api_user",
14951502
]
14961503
for key in str_keys:
14971504
if key in data:
@@ -1527,6 +1534,11 @@ def update_config(self, data: dict[str, Any], emit_log: bool = True) -> dict[str
15271534
data.get("hero_sms_auto_pick_country"),
15281535
bool(cfg.get("hero_sms_auto_pick_country", False)),
15291536
)
1537+
if "cf_routing_cleanup" in data:
1538+
cfg["cf_routing_cleanup"] = self._to_bool(
1539+
data.get("cf_routing_cleanup"),
1540+
bool(cfg.get("cf_routing_cleanup", True)),
1541+
)
15301542
if "mailfree_random_domain" in data:
15311543
cfg["mailfree_random_domain"] = self._to_bool(
15321544
data.get("mailfree_random_domain"),
@@ -1753,6 +1765,14 @@ def _apply_to_env(self) -> None:
17531765
os.environ["LUCKYOUS_SPECIFIED_EMAIL"] = str(
17541766
self.cfg.get("luckyous_specified_email") or ""
17551767
).strip().lower()
1768+
os.environ["CF_ROUTING_API_TOKEN"] = str(self.cfg.get("cf_routing_api_token") or "").strip()
1769+
os.environ["CF_ROUTING_ZONE_ID"] = str(self.cfg.get("cf_routing_zone_id") or "").strip()
1770+
os.environ["CF_ROUTING_DOMAIN"] = str(self.cfg.get("cf_routing_domain") or "").strip()
1771+
os.environ["CF_ROUTING_CLEANUP"] = "1" if self.cfg.get("cf_routing_cleanup", True) else "0"
1772+
os.environ["GMAIL_API_CLIENT_ID"] = str(self.cfg.get("gmail_api_client_id") or "").strip()
1773+
os.environ["GMAIL_API_CLIENT_SECRET"] = str(self.cfg.get("gmail_api_client_secret") or "").strip()
1774+
os.environ["GMAIL_API_REFRESH_TOKEN"] = str(self.cfg.get("gmail_api_refresh_token") or "").strip()
1775+
os.environ["GMAIL_API_USER"] = str(self.cfg.get("gmail_api_user") or "").strip()
17561776
os.environ["MAIL_SERVICE_PROVIDER"] = normalize_mail_provider(
17571777
self.cfg.get("mail_service_provider") or "mailfree"
17581778
)

0 commit comments

Comments
 (0)