🔄 卡若AI 同步 2026-03-15 19:47 | 更新:水溪整理归档、卡木、火炬、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个

This commit is contained in:
2026-03-15 19:47:32 +08:00
parent 8da96520d2
commit a80f33aeaf
28 changed files with 943 additions and 7 deletions

View File

@@ -0,0 +1,74 @@
# 全站埋点统计标准Soul 创业实验项目沉淀)
> 日期2026-03-15
> 来源Soul 创业实验项目 · 数据统计迭代
> 已写入:全栈开发 SKILL v2.5 § 1.10
---
## 背景
Soul 创业实验项目在管理后台需要「分类标签点击统计」面板,但发现小程序端没有埋点数据上报。从零实施全站埋点,覆盖 9 个小程序页面、后端聚合 API、管理后台可视化面板前后共 3 小时完成。
**教训**:功能上线后再补埋点,已丢失大量初期用户行为数据。**以后功能和埋点必须同时上线。**
---
## 标准三层架构
```
前端(小程序/Web 后端 API 管理后台
trackClick(module, → POST /api/{平台}/track → GET /api/admin/track/stats
action, target, extra) 存入 user_tracks 表 按 module/action/时间段聚合
```
### 1. 前端trackClick 工具
每个平台小程序、Web、App封装一个 `trackClick(module, action, target, extra?)` 函数:
- **module**所属模块home / chapters / read / my / vip / wallet / match / referral / search
- **action**标准动词btn_click / page_view / tab_click / nav_click / share
- **target**:具体按钮/链接名如「购买VIP」「充值」「阅读第3章」
- **extra**:可选扩展字段
静默上报,不弹窗不阻塞用户操作。
### 2. 后端track API + user_tracks 表
| 字段 | 类型 | 说明 |
|:---|:---|:---|
| id | string/UUID | 主键 |
| user_id | string | 用户 ID |
| action | string | 动作类型 |
| target | string | 具体目标 |
| extra_data | JSONB | 扩展信息,含 module、page |
| created_at | timestamp | 自动时间戳 |
注意 `extra_data` 要正确 Marshal 为 JSON 存入 JSONB 字段Go 端踩过坑:忘记 marshal body.ExtraData 导致 null
### 3. 管理后台:聚合面板
- 后端 `GET /api/admin/track/stats?period=today|week|month|all` 按 action+target+extra_data 聚合并分组
- 前端面板按 module 分组展示支持时间段切换30 秒自动刷新
---
## 埋点接入检查清单
每开发一个新功能/新页面,按此清单打勾:
- [ ] 页面所有可点击按钮/标签都已调用 trackClick
- [ ] module 参数使用统一命名
- [ ] action 参数使用标准动词
- [ ] target 参数能区分具体按钮
- [ ] 后端 track API 已注册路由且能正确存储 extra_data
- [ ] 管理后台能展示该模块数据
---
## 实际代码参考
- **小程序 trackClick**`miniprogram/utils/trackClick.js`
- **后端 track handler**`soul-api/internal/handler/admin_track.go`(聚合统计)、`soul-api/internal/handler/user.go`UserTrackPost 存储)
- **管理后台面板**`soul-admin/src/pages/dashboard/DashboardPage.tsx`(分类标签点击统计 Card
- **数据模型**`soul-api/internal/model/user_track.go`

View File

@@ -1,14 +1,14 @@
---
name: 全网API自动注册
description: 自动注册全网各类 AI/开发 API 免费账号,提取 API Key/Token 并统一管理
triggers: API注册、自动注册、批量注册、API Key、注册账号、免费API、API池、key池、自动开号
name: 全网AI自动注册
description: 自动注册全网各类 AI API 免费账号,提取 API Key/Token 并统一管理
triggers: AI注册、自动注册、批量注册、API Key、注册账号、免费API、API池、key池、自动开号、Gemini注册
owner: 木根
group: 木(卡木)
version: "1.0"
updated: "2026-03-15"
---
# 全网API自动注册
# 全网AI自动注册
## 能做什么Capabilities

View File

@@ -0,0 +1,320 @@
#!/usr/bin/env python3
"""
Gemini API Key 创建器(纯命令行 + 系统浏览器 OAuth 授权)
不需要打开额外浏览器,系统浏览器走 Clash 代理完成 Google 登录授权。
自动创建指定数量的 GCP 项目并生成 Gemini API Key。
用法: python3 gemini_key_creator.py --count 3
"""
import argparse
import http.server
import json
import os
import random
import string
import sys
import threading
import time
import urllib.parse
import webbrowser
from pathlib import Path
import httpx
PROXY = "http://127.0.0.1:7897"
# Google OAuth2 客户端(使用 Google Cloud SDK 的公开 client
CLIENT_ID = "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com"
CLIENT_SECRET = "d-FL95Q19q7MQmFpd7hHD0Ty"
REDIRECT_PORT = 18457
REDIRECT_URI = f"http://localhost:{REDIRECT_PORT}"
SCOPES = [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/generative-language",
]
TOKEN_URL = "https://oauth2.googleapis.com/token"
PROJECTS_URL = "https://cloudresourcemanager.googleapis.com/v1/projects"
SERVICES_URL = "https://serviceusage.googleapis.com/v1/projects/{project_id}/services/generativelanguage.googleapis.com:enable"
APIKEYS_URL = "https://apikeys.googleapis.com/v2/projects/{project_id}/locations/global/keys"
DB_PATH = Path(__file__).parent / "accounts.db"
JSON_DIR = Path(__file__).parent / "tokens"
auth_code_result = {"code": None}
class OAuthCallbackHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
qs = urllib.parse.parse_qs(parsed.query)
code = qs.get("code", [None])[0]
if code:
auth_code_result["code"] = code
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(b"<html><body><h2>&#10004; Google OAuth OK</h2><p>You can close this tab now.</p></body></html>")
else:
error = qs.get("error", ["unknown"])[0]
self.send_response(400)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(f"<html><body><h2>Error: {error}</h2></body></html>".encode())
def log_message(self, format, *args):
pass
def get_oauth_token():
"""OAuth 授权码流程:系统浏览器授权 → 获取 access_token"""
server = http.server.HTTPServer(("localhost", REDIRECT_PORT), OAuthCallbackHandler)
server.timeout = 300
thread = threading.Thread(target=server.handle_request, daemon=True)
thread.start()
auth_params = urllib.parse.urlencode({
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI,
"response_type": "code",
"scope": " ".join(SCOPES),
"access_type": "offline",
"prompt": "consent",
})
auth_url = f"https://accounts.google.com/o/oauth2/v2/auth?{auth_params}"
print(f"\n🔐 正在打开系统浏览器进行 Google 授权...")
print(f" (系统浏览器走 Clash 代理,无需额外操作)\n")
webbrowser.open(auth_url)
print("⏳ 等待授权回调...")
thread.join(timeout=300)
server.server_close()
code = auth_code_result["code"]
if not code:
print("❌ 授权超时或失败")
sys.exit(1)
print("✅ 授权码获取成功,兑换 Token...")
with httpx.Client(proxy=PROXY, timeout=20) as client:
resp = client.post(TOKEN_URL, data={
"code": code,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"redirect_uri": REDIRECT_URI,
"grant_type": "authorization_code",
})
if resp.status_code != 200:
print(f"❌ Token 兑换失败: {resp.status_code} {resp.text[:200]}")
sys.exit(1)
tokens = resp.json()
print(f"✅ Access Token 获取成功!")
return tokens["access_token"]
def create_project(token, project_id):
"""创建 GCP 项目"""
with httpx.Client(proxy=PROXY, timeout=30) as client:
resp = client.post(
PROJECTS_URL,
headers={"Authorization": f"Bearer {token}"},
json={"projectId": project_id, "name": project_id},
)
if resp.status_code in (200, 409):
if resp.status_code == 409:
print(f" 项目 {project_id} 已存在,继续使用")
return True
print(f" 创建项目失败: {resp.status_code} {resp.text[:200]}")
return False
def enable_gemini_api(token, project_id):
"""启用 Generative Language API"""
url = SERVICES_URL.format(project_id=project_id)
with httpx.Client(proxy=PROXY, timeout=30) as client:
resp = client.post(
url,
headers={"Authorization": f"Bearer {token}"},
)
if resp.status_code in (200, 409):
return True
print(f" 启用 API 失败: {resp.status_code} {resp.text[:200]}")
return False
def wait_for_operation(token, operation_name, max_wait=60):
"""等待长期运行操作完成"""
url = f"https://serviceusage.googleapis.com/v1/{operation_name}"
with httpx.Client(proxy=PROXY, timeout=15) as client:
for _ in range(max_wait // 3):
resp = client.get(url, headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
data = resp.json()
if data.get("done"):
return True
time.sleep(3)
return True
def create_api_key(token, project_id, display_name):
"""创建 API Key 并限制为 Generative Language API"""
url = APIKEYS_URL.format(project_id=project_id)
with httpx.Client(proxy=PROXY, timeout=30) as client:
resp = client.post(
url,
headers={"Authorization": f"Bearer {token}"},
json={
"displayName": display_name,
"restrictions": {
"apiTargets": [{"service": "generativelanguage.googleapis.com"}]
},
},
)
if resp.status_code == 200:
data = resp.json()
operation = data.get("name", "")
if "operations/" in operation:
time.sleep(3)
op_resp = client.get(
f"https://apikeys.googleapis.com/v2/{operation}",
headers={"Authorization": f"Bearer {token}"},
)
if op_resp.status_code == 200:
op_data = op_resp.json()
if op_data.get("done"):
key_data = op_data.get("response", {})
return key_data.get("keyString", "")
time.sleep(5)
op_resp2 = client.get(
f"https://apikeys.googleapis.com/v2/{operation}",
headers={"Authorization": f"Bearer {token}"},
)
if op_resp2.status_code == 200:
return op_resp2.json().get("response", {}).get("keyString", "")
return data.get("keyString", "")
print(f" 创建 Key 失败: {resp.status_code} {resp.text[:300]}")
return None
def list_existing_keys(token, project_id):
"""列出已有的 API Keys"""
url = APIKEYS_URL.format(project_id=project_id)
with httpx.Client(proxy=PROXY, timeout=15) as client:
resp = client.get(url, headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
return resp.json().get("keys", [])
return []
def save_key_to_db(api_key, project_id, idx):
"""保存到 SQLite"""
import sqlite3
JSON_DIR.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(str(DB_PATH)) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider TEXT NOT NULL,
email TEXT NOT NULL,
password TEXT DEFAULT '',
api_key TEXT DEFAULT '',
access_token TEXT DEFAULT '',
refresh_token TEXT DEFAULT '',
account_id TEXT DEFAULT '',
name TEXT DEFAULT '',
extra TEXT DEFAULT '{}',
registered_at TEXT NOT NULL,
status TEXT DEFAULT 'active',
UNIQUE(provider, email)
)
""")
from datetime import datetime, timezone
now = datetime.now(timezone.utc).isoformat()
conn.execute("""
INSERT OR REPLACE INTO accounts
(provider, email, api_key, name, extra, registered_at, status)
VALUES (?, ?, ?, ?, ?, ?, 'active')
""", ("gemini", f"gemini_project_{idx}@google.com", api_key,
project_id, json.dumps({"project": project_id}), now))
conn.commit()
json_file = JSON_DIR / f"gemini_{project_id}.json"
json_file.write_text(json.dumps({
"provider": "gemini",
"project": project_id,
"api_key": api_key,
}, indent=2))
print(f" 💾 已保存: DB + {json_file.name}")
def main():
global PROXY
parser = argparse.ArgumentParser(description="Gemini API Key 创建器")
parser.add_argument("--count", "-n", type=int, default=3, help="创建 Key 数量")
parser.add_argument("--proxy", default=PROXY, help="代理地址")
args = parser.parse_args()
PROXY = args.proxy
print("=" * 60)
print(f"🔑 Gemini API Key 创建器")
print(f" 目标: 创建 {args.count} 个 Gemini API Key")
print(f" 代理: {PROXY}")
print("=" * 60)
token = get_oauth_token()
keys_created = []
for i in range(1, args.count + 1):
suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
project_id = f"gemini-auto-{suffix}"
display_name = f"gemini-key-{i}"
print(f"\n--- 创建第 {i}/{args.count} 个 Key ---")
print(f" [1/3] 创建项目: {project_id}")
if not create_project(token, project_id):
print(f" ⚠️ 跳过此 Key")
continue
print(f" [2/3] 启用 Generative Language API")
time.sleep(2)
enable_gemini_api(token, project_id)
time.sleep(5)
print(f" [3/3] 创建 API Key: {display_name}")
api_key = create_api_key(token, project_id, display_name)
if api_key:
print(f" ✅ Key {i}: {api_key}")
keys_created.append({"key": api_key, "project": project_id})
save_key_to_db(api_key, project_id, i)
else:
print(f" ❌ Key 创建失败,尝试列出已有 Key...")
existing = list_existing_keys(token, project_id)
for k in existing:
ks = k.get("keyString")
if ks:
print(f" ✅ 找到已有 Key: {ks}")
keys_created.append({"key": ks, "project": project_id})
save_key_to_db(ks, project_id, i)
break
print(f"\n{'='*60}")
print(f"🎉 完成! 创建了 {len(keys_created)}/{args.count} 个 Gemini API Key")
for idx, kd in enumerate(keys_created, 1):
print(f" Key {idx}: {kd['key']}")
print(f" Project: {kd['project']}")
if keys_created:
print(f"\n测试命令:")
k = keys_created[0]["key"]
print(f' curl -x {PROXY} "https://generativelanguage.googleapis.com/v1beta/models?key={k}"')
print(f"\n存储: {DB_PATH} + {JSON_DIR}/")
print(f"{'='*60}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env python3
"""Google Device Flow 授权 + 创建 3 个 Gemini API Key"""
import httpx, time, json, sys, random, string, sqlite3
from pathlib import Path
from datetime import datetime, timezone
PROXY = "http://127.0.0.1:7897"
CLIENT_ID = "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com"
CLIENT_SECRET = "d-FL95Q19q7MQmFpd7hHD0Ty"
SCOPES = "https://www.googleapis.com/auth/cloud-platform"
DB_PATH = Path(__file__).parent / "accounts.db"
JSON_DIR = Path(__file__).parent / "tokens"
COUNT = int(sys.argv[1]) if len(sys.argv) > 1 else 3
def save_key(api_key, project_id, idx):
JSON_DIR.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(str(DB_PATH)) as conn:
conn.execute("""CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT, provider TEXT NOT NULL,
email TEXT NOT NULL, password TEXT DEFAULT '', api_key TEXT DEFAULT '',
access_token TEXT DEFAULT '', refresh_token TEXT DEFAULT '',
account_id TEXT DEFAULT '', name TEXT DEFAULT '', extra TEXT DEFAULT '{}',
registered_at TEXT NOT NULL, status TEXT DEFAULT 'active',
UNIQUE(provider, email))""")
now = datetime.now(timezone.utc).isoformat()
conn.execute("INSERT OR REPLACE INTO accounts (provider,email,api_key,name,extra,registered_at,status) VALUES (?,?,?,?,?,?,'active')",
("gemini", f"gemini_{project_id}@gcp", api_key, project_id, json.dumps({"project": project_id}), now))
conn.commit()
(JSON_DIR / f"gemini_{project_id}.json").write_text(json.dumps({"provider":"gemini","project":project_id,"api_key":api_key}, indent=2))
def main():
sep = "=" * 50
print(sep)
print("Gemini API Key Creator (Device Flow)")
print(f"Target: {COUNT} keys | Proxy: {PROXY}")
print(sep)
# Step 1: Device Flow
print("\n[Step 1] Google Device Flow...")
with httpx.Client(proxy=PROXY, timeout=15) as c:
r = c.post("https://oauth2.googleapis.com/device/code", data={"client_id": CLIENT_ID, "scope": SCOPES})
print(f"Status: {r.status_code}")
if r.status_code != 200:
print(f"Failed: {r.text[:300]}")
return
d = r.json()
user_code = d["user_code"]
verify_url = d["verification_url"]
device_code = d["device_code"]
interval = d.get("interval", 5)
print(f"\n{sep}")
print(f"Open in ANY browser/phone: {verify_url}")
print(f"Enter code: {user_code}")
print(f"{sep}\n")
print("Waiting for authorization...")
import webbrowser
webbrowser.open(verify_url)
token = None
for i in range(120):
time.sleep(interval)
tr = c.post("https://oauth2.googleapis.com/token", data={
"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET,
"device_code": device_code, "grant_type": "urn:ietf:params:oauth:grant-type:device_code"})
td = tr.json()
if "access_token" in td:
token = td["access_token"]
print(f"\nAuthorized! Token: {token[:25]}...")
break
err = td.get("error")
if err == "authorization_pending":
if i % 12 == 0 and i > 0:
print(f" Still waiting... ({i*interval}s)")
elif err == "slow_down":
time.sleep(5)
else:
print(f"Error: {td}")
return
if not token:
print("Timeout!")
return
# Step 2: Create projects and keys
print(f"\n[Step 2] Creating {COUNT} Gemini API Keys...")
keys = []
with httpx.Client(proxy=PROXY, timeout=30) as c:
headers = {"Authorization": f"Bearer {token}"}
for i in range(1, COUNT + 1):
sfx = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
pid = f"gemini-auto-{sfx}"
print(f"\n Key {i}/{COUNT}: project={pid}")
# Create project
pr = c.post("https://cloudresourcemanager.googleapis.com/v1/projects",
headers=headers, json={"projectId": pid, "name": pid})
print(f" Create project: {pr.status_code}")
if pr.status_code not in (200, 409):
print(f" Failed: {pr.text[:200]}")
continue
time.sleep(3)
# Enable API
er = c.post(f"https://serviceusage.googleapis.com/v1/projects/{pid}/services/generativelanguage.googleapis.com:enable",
headers=headers)
print(f" Enable API: {er.status_code}")
time.sleep(5)
# Create API key
kr = c.post(f"https://apikeys.googleapis.com/v2/projects/{pid}/locations/global/keys",
headers=headers, json={"displayName": f"gemini-key-{i}",
"restrictions": {"apiTargets": [{"service": "generativelanguage.googleapis.com"}]}})
print(f" Create key: {kr.status_code}")
if kr.status_code == 200:
kd = kr.json()
op_name = kd.get("name", "")
if "operations/" in op_name:
for _ in range(10):
time.sleep(3)
or2 = c.get(f"https://apikeys.googleapis.com/v2/{op_name}", headers=headers)
if or2.status_code == 200:
od = or2.json()
if od.get("done"):
ks = od.get("response", {}).get("keyString", "")
if ks:
print(f" KEY: {ks}")
keys.append({"key": ks, "project": pid})
save_key(ks, pid, i)
break
else:
ks = kd.get("keyString", "")
if ks:
print(f" KEY: {ks}")
keys.append({"key": ks, "project": pid})
save_key(ks, pid, i)
else:
print(f" Failed: {kr.text[:300]}")
print(f"\n{sep}")
print(f"Done! {len(keys)}/{COUNT} keys created")
for idx, k in enumerate(keys, 1):
print(f" {idx}. {k['key']} (project: {k['project']})")
if keys:
print(f"\nTest: curl -x {PROXY} 'https://generativelanguage.googleapis.com/v1beta/models?key={keys[0]['key']}'")
print(sep)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env python3
"""
Gemini API Key 自动注册脚本
1. 通过 Clash 代理打开 Google AI Studio非无头模式你手动登录 Google
2. 登录完成后脚本自动创建指定数量的 API Key
3. Key 存入本地 SQLite + JSON
用法: python3 register_gemini_keys.py --count 3
"""
import argparse
import json
import os
import re
import sys
import time
from pathlib import Path
from datetime import datetime, timezone
PROXY_SERVER = "http://127.0.0.1:7897"
AI_STUDIO_URL = "https://aistudio.google.com/app/apikey"
DB_PATH = Path(__file__).parent / "accounts.db"
JSON_DIR = Path(__file__).parent / "tokens"
def save_key(provider, email, api_key, project_name=""):
"""保存到 SQLite + JSON"""
import sqlite3
JSON_DIR.mkdir(parents=True, exist_ok=True)
with sqlite3.connect(str(DB_PATH)) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider TEXT NOT NULL,
email TEXT NOT NULL,
password TEXT DEFAULT '',
api_key TEXT DEFAULT '',
access_token TEXT DEFAULT '',
refresh_token TEXT DEFAULT '',
account_id TEXT DEFAULT '',
name TEXT DEFAULT '',
extra TEXT DEFAULT '{}',
registered_at TEXT NOT NULL,
status TEXT DEFAULT 'active',
UNIQUE(provider, email)
)
""")
now = datetime.now(timezone.utc).isoformat()
conn.execute("""
INSERT OR REPLACE INTO accounts
(provider, email, api_key, name, extra, registered_at, status)
VALUES (?, ?, ?, ?, ?, ?, 'active')
""", (provider, email, api_key, project_name, json.dumps({"project": project_name}), now))
conn.commit()
safe_email = email.replace("@", "_at_")
json_file = JSON_DIR / f"gemini_{safe_email}_{int(time.time())}.json"
json_file.write_text(json.dumps({
"provider": "gemini",
"email": email,
"api_key": api_key,
"project": project_name,
"registered_at": datetime.now(timezone.utc).isoformat(),
}, indent=2, ensure_ascii=False))
print(f" [保存] {json_file.name}")
def main():
parser = argparse.ArgumentParser(description="Gemini API Key 自动注册")
parser.add_argument("--count", "-n", type=int, default=3, help="要创建的 Key 数量")
parser.add_argument("--proxy", default=PROXY_SERVER, help="代理地址")
args = parser.parse_args()
from playwright.sync_api import sync_playwright
print(f"🚀 启动浏览器(代理: {args.proxy}")
print(f"📋 目标: 创建 {args.count} 个 Gemini API Key\n")
pw = sync_playwright().start()
browser = pw.chromium.launch(
headless=False,
proxy={"server": args.proxy},
args=["--window-size=1280,900"],
)
context = browser.new_context(
viewport={"width": 1280, "height": 900},
locale="en-US",
)
page = context.new_page()
print("📂 打开 Google AI Studio...")
page.goto(AI_STUDIO_URL, timeout=30000)
time.sleep(2)
if "accounts.google.com" in page.url:
print("\n" + "=" * 60)
print("⚠️ 请在弹出的浏览器窗口中登录你的 Google 账号")
print(" 登录完成后脚本会自动继续...")
print("=" * 60 + "\n")
for i in range(300):
time.sleep(2)
current = page.url
# 必须是真正到达 aistudio而非 accounts.google.com 里的 continue 参数
if current.startswith("https://aistudio.google.com"):
print("✅ 登录成功!已进入 AI Studio")
break
if i % 15 == 0 and i > 0:
print(f" 等待登录中... ({i*2}秒)")
else:
print("❌ 登录超时10分钟请重试")
browser.close()
pw.stop()
return
time.sleep(3)
print(f"\n当前页面: {page.url}")
if "/apikey" not in page.url:
page.goto(AI_STUDIO_URL, timeout=20000)
time.sleep(3)
keys_created = []
for i in range(args.count):
print(f"\n--- 创建第 {i+1}/{args.count} 个 Key ---")
try:
page.wait_for_load_state("networkidle", timeout=10000)
except Exception:
pass
create_btn = None
for selector in [
"button:has-text('Create API key')",
"button:has-text('Create API Key')",
"button:has-text('创建 API 密钥')",
"[aria-label='Create API key']",
"button:has-text('Get API key')",
"button:has-text('获取 API 密钥')",
]:
try:
btn = page.locator(selector).first
if btn.is_visible(timeout=3000):
create_btn = btn
break
except Exception:
continue
if not create_btn:
print(" ⚠️ 未找到 Create API Key 按钮,尝试截图诊断...")
page.screenshot(path=str(JSON_DIR / f"debug_step_{i+1}.png"))
snapshot = page.content()
if "Create" in snapshot or "创建" in snapshot:
print(" 页面包含创建按钮文字,尝试通用点击...")
try:
page.locator("button").filter(has_text=re.compile(r"Create|创建|Get|获取")).first.click()
except Exception as e:
print(f" 点击失败: {e}")
continue
else:
print(f" 页面 URL: {page.url}")
continue
else:
create_btn.click()
time.sleep(2)
new_project_btn = None
for selector in [
"button:has-text('Create API key in new project')",
"button:has-text('在新项目中创建 API 密钥')",
"text='Create API key in new project'",
"button:has-text('new project')",
]:
try:
btn = page.locator(selector).first
if btn.is_visible(timeout=3000):
new_project_btn = btn
break
except Exception:
continue
if new_project_btn:
new_project_btn.click()
print(" 点击了 'Create in new project'")
else:
existing_btns = page.locator("button:has-text('Create')").all()
for btn in existing_btns:
try:
if btn.is_visible():
btn.click()
break
except Exception:
continue
time.sleep(5)
api_key = None
for attempt in range(10):
page_text = page.content()
matches = re.findall(r'AIzaSy[A-Za-z0-9_-]{33}', page_text)
if matches:
api_key = matches[0]
break
try:
code_el = page.locator("code, .api-key, [data-testid*='key'], pre").first
if code_el.is_visible(timeout=1000):
text = code_el.inner_text()
m = re.search(r'AIzaSy[A-Za-z0-9_-]{33}', text)
if m:
api_key = m.group(0)
break
except Exception:
pass
copy_btns = page.locator("button:has-text('Copy')").all()
if not copy_btns:
copy_btns = page.locator("button:has-text('复制')").all()
for btn in copy_btns:
try:
if btn.is_visible():
btn.click()
time.sleep(0.5)
break
except Exception:
pass
time.sleep(1)
if attempt % 3 == 2:
print(f" 等待 Key 生成... (尝试 {attempt+1})")
if api_key:
print(f" ✅ Key {i+1}: {api_key[:20]}...")
keys_created.append(api_key)
save_key("gemini", f"gemini_project_{i+1}@google.com", api_key, f"project_{i+1}")
else:
print(f" ❌ 未能提取到 API Key请检查浏览器窗口")
page.screenshot(path=str(JSON_DIR / f"failed_key_{i+1}.png"))
close_btns = page.locator("button:has-text('Done'), button:has-text('Close'), button:has-text('完成'), button:has-text('关闭')").all()
for btn in close_btns:
try:
if btn.is_visible():
btn.click()
break
except Exception:
pass
time.sleep(2)
if "/apikey" not in page.url:
page.goto(AI_STUDIO_URL, timeout=15000)
time.sleep(3)
print(f"\n{'='*60}")
print(f"🎉 完成! 成功创建 {len(keys_created)}/{args.count} 个 Gemini API Key")
for idx, key in enumerate(keys_created, 1):
print(f" Key {idx}: {key}")
print(f"\n存储位置: {DB_PATH}")
print(f"JSON 目录: {JSON_DIR}")
print(f"{'='*60}")
input("\n按 Enter 关闭浏览器...")
browser.close()
pw.stop()
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_1@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_1",
"registered_at": "2026-03-15T11:21:28.308260+00:00"
}

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_1@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_1",
"registered_at": "2026-03-15T11:22:40.294576+00:00"
}

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_2@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_2",
"registered_at": "2026-03-15T11:21:42.299331+00:00"
}

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_2@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_2",
"registered_at": "2026-03-15T11:22:54.880143+00:00"
}

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_3@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_3",
"registered_at": "2026-03-15T11:21:57.080669+00:00"
}

View File

@@ -0,0 +1,7 @@
{
"provider": "gemini",
"email": "gemini_project_3@google.com",
"api_key": "AIzaSyA6RHlWHTfUEqMzdfQQQSmugsGcoDIxnAg",
"project": "project_3",
"registered_at": "2026-03-15T11:23:09.014341+00:00"
}

View File

@@ -4,8 +4,8 @@ description: 卡若AI 全栈开发(火炬)— 知己及类似项目经验 +
triggers: 全栈开发/知己项目/分销/存客宝/RAG/向量化/Next.js/知识库/卡若AI官网/官网开发/全站开发/开发文档/110/开发模板/官网全站/v0前端/v0生成/毛玻璃/前端规格/神射手/毛狐狸/前端标准/实施计划/两阶段评审/橙色锁/配色/API调用/使用手册
owner: 火炬
group: 火
version: "2.4"
updated: "2026-03-12"
version: "2.5"
updated: "2026-03-15"
---
# 全栈开发(火炬)
@@ -125,6 +125,75 @@ updated: "2026-03-12"
**协同**Word/文档类清洗用火炬「文档清洗」;部署到 v0、同步 GitHub/Vercel 用金盾「Vercel与v0部署流水线」。
### 1.10 埋点统计全站强制2026-03-15 沉淀)
**任何新功能、新页面、新按钮上线时,必须同步接入埋点统计。** 这是全站开发的标准动作,与功能代码同等重要。
#### 为什么强制
没有埋点 = 没有数据 = 无法判断功能是否有效。上线后再补埋点往往遗漏大量初期行为数据。**先埋点、再发布** 是卡若AI 全站的铁律。
#### 标准架构(三层)
```
前端(小程序/Web 后端 API 管理后台
trackClick(module, → POST /api/{平台}/track → GET /api/admin/track/stats
action, target, extra) 存入 user_tracks 表 按 module/action/时间段聚合
```
#### 数据模型user_tracks 表)
| 字段 | 类型 | 说明 |
|:---|:---|:---|
| id | string/UUID | 主键 |
| user_id | string | 用户 ID |
| action | string | 动作类型:`btn_click` / `page_view` / `tab_click` / `share` / `nav_click` |
| target | string | 具体目标:按钮名、页面名、分享类型 |
| extra_data | JSONB | 扩展信息,**必须包含 `module`(所属模块)和 `page`(页面标识)** |
| created_at | timestamp | 自动时间戳 |
#### 前端埋点工具标准(以小程序为例)
```javascript
// utils/trackClick.js
function trackClick(module, action, target, extra) {
const userId = app.globalData.userInfo?.id || ''
if (!userId) return
app.request('/api/miniprogram/track', {
method: 'POST',
data: {
userId, action, target,
extraData: Object.assign({ module, page: module }, extra || {})
},
silent: true
}).catch(() => {})
}
```
Web 端同理,封装为 `trackClick(module, action, target)` 函数,通过 fetch 静默上报。
#### 埋点接入检查清单
每开发一个页面/功能,按此清单逐项确认:
- [ ] 页面所有**可点击按钮/标签**都已调用 `trackClick`
- [ ] `module` 参数使用统一命名home / chapters / read / my / vip / wallet / match / referral / search / settings
- [ ] `action` 参数使用标准动词(`btn_click` / `page_view` / `tab_click` / `nav_click` / `share`
- [ ] `target` 参数能区分具体按钮(如 `购买VIP``充值``阅读第3章`
- [ ] 后端 track API 已注册路由并能正确存储 `extra_data`
- [ ] 管理后台「分类标签点击统计」面板能展示该模块的数据
#### 管理后台展示标准
管理后台数据概览页须包含「分类标签点击统计」面板:
- 支持时间段筛选(今日 / 本周 / 本月 / 全部)
- 按 module 分组展示,每个模块显示 top N 点击项
- 自动 30 秒刷新
#### 经验来源
Soul 创业实验项目2026-03-15首次实施全站埋点覆盖小程序 9 个页面 + 管理后台统计面板 + 后端聚合 API。详见 `运营中枢/参考资料/项目经验库_知己与类似项目.md`
---
## 二、项目经验库(知己类,必读)
@@ -190,6 +259,7 @@ scripts/
| **前端开发/前端标准_神射手与毛狐狸** | 布局/颜色/毛玻璃/组件/特效统一标准,所有项目前端开发参考 |
| **神射手 开发文档 4、前端** | 神射手项目内前端规范、核心组件代码、截图索引 |
| **Superpowers与全栈开发对比与优化建议** | `运营中枢/参考资料/Superpowers与全栈开发对比与优化建议.md` — 计划粒度、TDD、两阶段评审、分支收尾等优化方向 |
| **埋点统计标准Soul项目沉淀** | 全站埋点三层架构:前端 trackClick → 后端 track API → 管理后台聚合面板2026-03-15 Soul 创业实验项目首次实施,见本 Skill 1.10 节 |
---

View File

@@ -110,7 +110,7 @@
| M01g | 快手发布 | 木叶 | **快手发布、发布到快手、快手登录、快手上传、kuaishou发布** | `03_卡木/木叶_视频内容/快手发布/SKILL.md` | 逆向 cp.kuaishou.com API 视频发布 |
| M01h | 多平台分发 | 木叶 | **多平台分发、一键分发、全平台发布、批量分发、视频分发** | `03_卡木/木叶_视频内容/多平台分发/SKILL.md` | 一键分发到5平台抖音/B站/视频号/小红书/快手Cookie统一管理 |
| M02 | 网站逆向分析 | 木根 | 逆向分析、模拟登录 | `03_卡木/木根_逆向分析/网站逆向分析/SKILL.md` | 网站 API 分析、SDK 生成 |
| M02a | **全网API自动注册** | 木根 | **API注册、自动注册、批量注册、API Key、注册账号、免费API、API池、key池、自动开号** | `03_卡木/木根_逆向分析/全网API自动注册/SKILL.md` | OpenAI/Cursor/Gemini/Groq 等全网 API 自动注册+Key 池管理 |
| M02a | **全网AI自动注册** | 木根 | **AI注册、自动注册、批量注册、API Key、注册账号、免费API、API池、key池、自动开号、Gemini注册** | `03_卡木/木根_逆向分析/全网AI自动注册/SKILL.md` | OpenAI/Cursor/Gemini/Groq 等全网 AI API 自动注册+Key 池管理 |
| M03 | 项目生成 | 木果 | 生成项目、五行模板 | `03_卡木/木果_项目模板/项目生成/SKILL.md` | 按五行模板生成新项目 |
| M04 | 开发模板 | 木果 | 创建项目、初始化模板 | `03_卡木/木果_项目模板/开发模板/SKILL.md` | 前后端项目模板库 |
| M05 | 个人档案生成器 | 木果 | 个人档案、档案生成 | `03_卡木/木果_项目模板/个人档案生成器/SKILL.md` | 自动生成个人介绍档案 |

View File

@@ -360,3 +360,4 @@
| 2026-03-15 12:31:12 | 🔄 卡若AI 同步 2026-03-15 12:31 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-15 15:48:32 | 🔄 卡若AI 同步 2026-03-15 15:48 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-15 16:00:52 | 🔄 卡若AI 同步 2026-03-15 16:00 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-15 19:00:56 | 🔄 卡若AI 同步 2026-03-15 19:00 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个 |

View File

@@ -363,3 +363,4 @@
| 2026-03-15 12:31:12 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-15 12:31 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-15 15:48:32 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-15 15:48 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-15 16:00:52 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-15 16:00 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-15 19:00:56 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-15 19:00 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |