Initial commit: Captcha Service v1.0.0
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
.env
|
||||||
|
*.key
|
||||||
|
|
||||||
|
# System files
|
||||||
|
Thumbs.db
|
||||||
|
.DS_Store
|
||||||
|
desktop.ini
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 DVS (dvs-dvsxt)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# 🔢 Captcha Service — Verification Code Generation & Verification
|
||||||
|
|
||||||
|
> A Flask captcha/verification-code service with cookie binding, token flow, rate limiting, and code expiry.
|
||||||
|
|
||||||
|
**Captcha Service** implements a complete verification-code (captcha) flow: issue a code bound to a client cookie, verify it, and issue a short-lived token for further authorization. Includes rate limiting and expiry for security.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| 🍪 **Cookie Binding** | Codes are bound to client cookies & IP |
|
||||||
|
| 🔢 **Code Generation** | Generates verification codes with configurable expiry (default 120s) |
|
||||||
|
| 🔑 **Token Flow** | Issues short-lived tokens (default 300s) after code verification |
|
||||||
|
| ⏱️ **Rate Limiting** | Prevents spam (default 60s between requests) |
|
||||||
|
| 🏥 **Health Check** | `/health` endpoint for monitoring |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 API Endpoints
|
||||||
|
|
||||||
|
### POST `/apply`
|
||||||
|
Apply for a captcha (obtain a code).
|
||||||
|
- **Params**: client cookie / info
|
||||||
|
- **Returns**: captcha code & cookie binding
|
||||||
|
|
||||||
|
### POST `/request_code`
|
||||||
|
Request a verification code.
|
||||||
|
- **Params**: cookie / IP / target
|
||||||
|
- **Returns**: code (subject to rate limit)
|
||||||
|
|
||||||
|
### POST `/verify`
|
||||||
|
Verify a code.
|
||||||
|
- **Params**: cookie, code
|
||||||
|
- **Returns**: success / failure
|
||||||
|
|
||||||
|
### POST `/check_token`
|
||||||
|
Validate an issued token.
|
||||||
|
- **Params**: token
|
||||||
|
- **Returns**: token validity + bound info
|
||||||
|
|
||||||
|
### GET `/health`
|
||||||
|
Health check.
|
||||||
|
- **Returns**: `{status: ok}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Python 3.7+
|
||||||
|
- `flask`
|
||||||
|
|
||||||
|
### Configure
|
||||||
|
|
||||||
|
Edit the config section in `captcha_service.py`:
|
||||||
|
```python
|
||||||
|
VERIFY_CODE_EXPIRE_SECONDS = 120 # code expiry (s)
|
||||||
|
RATE_LIMIT_SECONDS = 60 # request interval limit (s)
|
||||||
|
TOKEN_EXPIRE_SECONDS = 300 # token expiry (s)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python captcha_service.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗂️ Storage (in-memory)
|
||||||
|
|
||||||
|
| Store | Purpose |
|
||||||
|
|-------|---------|
|
||||||
|
| `COOKIE_STORE` | Tracks cookies → cudid / ip / created_at |
|
||||||
|
| `CODE_STORE` | Holds issued codes with expiry & last-request |
|
||||||
|
| `TOKEN_STORE` | Holds issued tokens with expiry & usage status |
|
||||||
|
| `REQUEST_RECORD` | Rate-limiting record by cookie / IP |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
Licensed under the **MIT License**. See [LICENSE](LICENSE).
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# ==================== 配置 ====================
|
||||||
|
VERIFY_CODE_EXPIRE_SECONDS = 120 # 验证码过期时间(秒)
|
||||||
|
RATE_LIMIT_SECONDS = 60 # 申请间隔限制(秒)
|
||||||
|
TOKEN_EXPIRE_SECONDS = 300 # token过期时间(秒)
|
||||||
|
|
||||||
|
# ==================== AID ====================
|
||||||
|
AID = "填写"
|
||||||
|
# ==================== 存储 ====================
|
||||||
|
COOKIE_STORE = {} # {cookie: {'cudid': xxx, 'ip': xxx, 'created_at': timestamp}}
|
||||||
|
CODE_STORE = {} # {cookie: {'code': xxx, 'expire_at': timestamp, 'last_request': timestamp}}
|
||||||
|
TOKEN_STORE = {} # {token: {'cookie': xxx, 'cudid': xxx, 'expire_at': timestamp, 'used': False, 'code': xxx}}
|
||||||
|
REQUEST_RECORD = {} # {key: timestamp} key可以是cookie或ip
|
||||||
|
|
||||||
|
# ==================== 工具函数 ====================
|
||||||
|
|
||||||
|
def generate_cookie():
|
||||||
|
"""生成高熵Cookie"""
|
||||||
|
return secrets.token_hex(64) + secrets.token_hex(32)
|
||||||
|
|
||||||
|
def generate_verify_code():
|
||||||
|
"""生成10位高熵动态验证密码"""
|
||||||
|
chars = string.ascii_letters + string.digits + "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||||
|
return ''.join(secrets.choice(chars) for _ in range(10))
|
||||||
|
|
||||||
|
def generate_token():
|
||||||
|
"""生成32位一次性token"""
|
||||||
|
return secrets.token_hex(32)
|
||||||
|
|
||||||
|
def build_verify_email_html(code):
|
||||||
|
"""构建验证码邮件HTML"""
|
||||||
|
return f"""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><meta charset="UTF-8"></head>
|
||||||
|
<body style="margin:0;padding:0;background:#f0f2f3;">
|
||||||
|
<table cellspacing="0" cellpadding="0" width="100%" align="center" style="max-width:600px;margin:0 auto;background:#fff;font-family:Arial;font-size:14px;color:#444;border-bottom:1px solid #d6d6d6;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:25px 35px;">
|
||||||
|
<h1 style="font-size:20px;font-weight:bold;margin:0 0 15px;">验证您的电子邮件地址。</h1>
|
||||||
|
<p style="margin:0 0 15px;">感谢您注册我们的账户。为确保当前是您本人操作,请您输入此邮件中提示的验证码以完成账户注册。如您没有注册账户,请放心忽略该信息。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 35px 25px;text-align:center;">
|
||||||
|
<div style="font-weight:bold;padding-bottom:15px;color:#444;">DVSWebSVC-动态验证密码</div>
|
||||||
|
<div style="color:#000;font-size:36px;font-weight:bold;padding-bottom:15px;letter-spacing:4px;">{code}</div>
|
||||||
|
<div style="color:#666;font-size:13px;">(此验证码将在发送后 {VERIFY_CODE_EXPIRE_SECONDS} 秒过期。)</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def send_email_via_local(to_email, html_content):
|
||||||
|
"""调用本地:5000/send 发送邮件"""
|
||||||
|
try:
|
||||||
|
login_resp = requests.post(
|
||||||
|
"http://localhost:5000/login",
|
||||||
|
json={
|
||||||
|
"aid": AID,
|
||||||
|
"username": "dvsadmin",
|
||||||
|
"password": "G7#kLp$QwZ@2xR&yM9!nVb*C5^jHf%T"
|
||||||
|
},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if login_resp.status_code != 200:
|
||||||
|
return False, "登录本地邮件服务失败"
|
||||||
|
|
||||||
|
result = login_resp.json()
|
||||||
|
if result.get('code') != 200:
|
||||||
|
return False, f"登录失败: {result.get('message')}"
|
||||||
|
|
||||||
|
cookie = result.get('cookie')
|
||||||
|
if not cookie:
|
||||||
|
return False, "获取邮件服务cookie失败"
|
||||||
|
|
||||||
|
send_resp = requests.post(
|
||||||
|
"http://localhost:5000/send",
|
||||||
|
json={
|
||||||
|
"cookie": cookie,
|
||||||
|
"to": to_email,
|
||||||
|
"subject": "【DVSWebSVC】验证您的电子邮件地址",
|
||||||
|
"content": html_content
|
||||||
|
},
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
if send_resp.status_code == 200:
|
||||||
|
result = send_resp.json()
|
||||||
|
if result.get('code') == 200:
|
||||||
|
return True, "邮件发送成功"
|
||||||
|
else:
|
||||||
|
return False, result.get('message', '邮件发送失败')
|
||||||
|
else:
|
||||||
|
return False, f"邮件服务返回错误: {send_resp.status_code}"
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return False, "调用邮件服务超时"
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return False, "无法连接到邮件服务 (localhost:5000)"
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"调用邮件服务异常: {str(e)}"
|
||||||
|
|
||||||
|
# ==================== API 接口 ====================
|
||||||
|
|
||||||
|
@app.route('/apply', methods=['POST'])
|
||||||
|
def apply_cookie():
|
||||||
|
"""
|
||||||
|
申请Cookie(只需CUDID)
|
||||||
|
请求体: {"cudid": "256位设备唯一标识符"}
|
||||||
|
返回: {"code": 200, "cookie": "xxx", "message": "申请成功"}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.get_json() or {}
|
||||||
|
cudid = data.get('cudid')
|
||||||
|
|
||||||
|
# 验证CUDID
|
||||||
|
if not cudid:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 cudid"}), 400
|
||||||
|
|
||||||
|
# 验证CUDID是否为256位哈希(64位十六进制字符)
|
||||||
|
if len(cudid) != 64:
|
||||||
|
return jsonify({"code": 400, "message": "cudid 必须是64位十六进制字符串(256位)"}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
int(cudid, 16) # 验证是否为十六进制
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({"code": 400, "message": "cudid 必须是有效的十六进制字符串"}), 400
|
||||||
|
|
||||||
|
# 生成永久Cookie
|
||||||
|
cookie = generate_cookie()
|
||||||
|
|
||||||
|
# 存储cookie信息
|
||||||
|
COOKIE_STORE[cookie] = {
|
||||||
|
'cudid': cudid,
|
||||||
|
'ip': request.remote_addr,
|
||||||
|
'created_at': time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"code": 200,
|
||||||
|
"message": "申请成功",
|
||||||
|
"cookie": cookie
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"code": 500, "message": f"服务器错误: {str(e)}"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/request_code', methods=['POST'])
|
||||||
|
def request_verify_code():
|
||||||
|
"""
|
||||||
|
申请验证码
|
||||||
|
请求体: {"cookie": "xxx", "cudid": "256位设备标识符", "to": "收件邮箱"}
|
||||||
|
返回: {"code": 200, "token": "xxx", "message": "验证码已发送"}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({"code": 400, "message": "请求体不能为空"}), 400
|
||||||
|
|
||||||
|
cookie = data.get('cookie')
|
||||||
|
cudid = data.get('cudid')
|
||||||
|
to_email = data.get('to')
|
||||||
|
|
||||||
|
# 参数校验
|
||||||
|
if not cookie:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 cookie"}), 400
|
||||||
|
if not cudid:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 cudid"}), 400
|
||||||
|
if not to_email:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 to"}), 400
|
||||||
|
|
||||||
|
# 验证CUDID格式
|
||||||
|
if len(cudid) != 64:
|
||||||
|
return jsonify({"code": 400, "message": "cudid 必须是64位十六进制字符串(256位)"}), 400
|
||||||
|
try:
|
||||||
|
int(cudid, 16)
|
||||||
|
except ValueError:
|
||||||
|
return jsonify({"code": 400, "message": "cudid 必须是有效的十六进制字符串"}), 400
|
||||||
|
|
||||||
|
# 验证Cookie是否存在
|
||||||
|
if cookie not in COOKIE_STORE:
|
||||||
|
return jsonify({"code": 401, "message": "Cookie无效,请重新申请"}), 401
|
||||||
|
|
||||||
|
# 验证CUDID是否匹配
|
||||||
|
stored = COOKIE_STORE[cookie]
|
||||||
|
if stored.get('cudid') != cudid:
|
||||||
|
return jsonify({"code": 401, "message": "CUDID不匹配"}), 401
|
||||||
|
|
||||||
|
# 限流检查(IP或Cookie任一限制)
|
||||||
|
ip = request.remote_addr
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
# 检查IP限制
|
||||||
|
if ip in REQUEST_RECORD:
|
||||||
|
if now - REQUEST_RECORD[ip] < RATE_LIMIT_SECONDS:
|
||||||
|
wait = int(RATE_LIMIT_SECONDS - (now - REQUEST_RECORD[ip]))
|
||||||
|
return jsonify({"code": 429, "message": f"IP限制,请等待 {wait} 秒"}), 429
|
||||||
|
|
||||||
|
# 检查Cookie限制
|
||||||
|
if cookie in REQUEST_RECORD:
|
||||||
|
if now - REQUEST_RECORD[cookie] < RATE_LIMIT_SECONDS:
|
||||||
|
wait = int(RATE_LIMIT_SECONDS - (now - REQUEST_RECORD[cookie]))
|
||||||
|
return jsonify({"code": 429, "message": f"Cookie限制,请等待 {wait} 秒"}), 429
|
||||||
|
|
||||||
|
# 生成验证码
|
||||||
|
code = generate_verify_code()
|
||||||
|
|
||||||
|
# 构建HTML邮件
|
||||||
|
html_content = build_verify_email_html(code)
|
||||||
|
|
||||||
|
# 发送邮件
|
||||||
|
success, msg = send_email_via_local(to_email, html_content)
|
||||||
|
if not success:
|
||||||
|
return jsonify({"code": 500, "message": f"发送失败: {msg}"}), 500
|
||||||
|
|
||||||
|
# 记录申请时间(IP和Cookie都记录)
|
||||||
|
REQUEST_RECORD[ip] = now
|
||||||
|
REQUEST_RECORD[cookie] = now
|
||||||
|
|
||||||
|
# 存储验证码
|
||||||
|
CODE_STORE[cookie] = {
|
||||||
|
'code': code,
|
||||||
|
'expire_at': now + VERIFY_CODE_EXPIRE_SECONDS,
|
||||||
|
'last_request': now
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成一次性token
|
||||||
|
token = generate_token()
|
||||||
|
TOKEN_STORE[token] = {
|
||||||
|
'cookie': cookie,
|
||||||
|
'cudid': cudid,
|
||||||
|
'code': code,
|
||||||
|
'expire_at': now + TOKEN_EXPIRE_SECONDS,
|
||||||
|
'used': False
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"code": 200,
|
||||||
|
"message": "验证码已发送",
|
||||||
|
"token": token
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"code": 500, "message": f"服务器错误: {str(e)}"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/verify', methods=['POST'])
|
||||||
|
def verify_code():
|
||||||
|
"""
|
||||||
|
验证验证码
|
||||||
|
请求体: {"token": "xxx", "code": "用户输入的验证码"}
|
||||||
|
返回: {"code": 200, "message": "验证成功"}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({"code": 400, "message": "请求体不能为空"}), 400
|
||||||
|
|
||||||
|
token = data.get('token')
|
||||||
|
user_code = data.get('code')
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 token"}), 400
|
||||||
|
if not user_code:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 code"}), 400
|
||||||
|
|
||||||
|
# 验证token是否存在
|
||||||
|
if token not in TOKEN_STORE:
|
||||||
|
return jsonify({"code": 401, "message": "Token无效"}), 401
|
||||||
|
|
||||||
|
token_data = TOKEN_STORE[token]
|
||||||
|
|
||||||
|
# 检查是否已使用
|
||||||
|
if token_data.get('used'):
|
||||||
|
return jsonify({"code": 401, "message": "Token已使用"}), 401
|
||||||
|
|
||||||
|
# 检查是否过期
|
||||||
|
if time.time() > token_data.get('expire_at', 0):
|
||||||
|
TOKEN_STORE[token]['used'] = True
|
||||||
|
return jsonify({"code": 401, "message": "Token已过期"}), 401
|
||||||
|
|
||||||
|
# 验证验证码
|
||||||
|
stored_code = token_data.get('code')
|
||||||
|
if user_code != stored_code:
|
||||||
|
return jsonify({"code": 401, "message": "验证码错误"}), 401
|
||||||
|
|
||||||
|
# 验证成功,标记token已使用
|
||||||
|
TOKEN_STORE[token]['used'] = True
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"code": 200,
|
||||||
|
"message": "验证成功"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"code": 500, "message": f"服务器错误: {str(e)}"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/check_token', methods=['POST'])
|
||||||
|
def check_token():
|
||||||
|
"""
|
||||||
|
检查token状态
|
||||||
|
请求体: {"token": "xxx"}
|
||||||
|
返回: {"code": 200, "valid": true/false, "used": true/false, "expired": true/false}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.get_json() or {}
|
||||||
|
token = data.get('token')
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
return jsonify({"code": 400, "message": "缺少 token"}), 400
|
||||||
|
|
||||||
|
if token not in TOKEN_STORE:
|
||||||
|
return jsonify({"code": 200, "valid": False, "message": "Token不存在"})
|
||||||
|
|
||||||
|
token_data = TOKEN_STORE[token]
|
||||||
|
is_expired = time.time() > token_data.get('expire_at', 0)
|
||||||
|
is_used = token_data.get('used', False)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"code": 200,
|
||||||
|
"valid": not is_expired and not is_used,
|
||||||
|
"used": is_used,
|
||||||
|
"expired": is_expired
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"code": 500, "message": f"服务器错误: {str(e)}"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/health', methods=['GET'])
|
||||||
|
def health():
|
||||||
|
"""健康检查"""
|
||||||
|
return jsonify({
|
||||||
|
"code": 200,
|
||||||
|
"message": "服务运行正常",
|
||||||
|
"stats": {
|
||||||
|
"cookies": len(COOKIE_STORE),
|
||||||
|
"codes": len(CODE_STORE),
|
||||||
|
"tokens": len(TOKEN_STORE),
|
||||||
|
"records": len(REQUEST_RECORD)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("=" * 60)
|
||||||
|
print("DVSWebSVC 验证码服务")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"验证码有效期: {VERIFY_CODE_EXPIRE_SECONDS} 秒")
|
||||||
|
print(f"限流间隔: {RATE_LIMIT_SECONDS} 秒")
|
||||||
|
print(f"Token有效期: {TOKEN_EXPIRE_SECONDS} 秒")
|
||||||
|
print("=" * 60)
|
||||||
|
print("\n📍 申请Cookie: POST /apply")
|
||||||
|
print(" 请求: {\"cudid\": \"64位十六进制(256位)\"}")
|
||||||
|
print(" 返回: {\"code\": 200, \"cookie\": \"xxx\"}")
|
||||||
|
print("\n📍 申请验证码: POST /request_code")
|
||||||
|
print(" 请求: {\"cookie\": \"xxx\", \"cudid\": \"xxx\", \"to\": \"邮箱\"}")
|
||||||
|
print(" 返回: {\"code\": 200, \"token\": \"xxx\"}")
|
||||||
|
print("\n📍 验证验证码: POST /verify")
|
||||||
|
print(" 请求: {\"token\": \"xxx\", \"code\": \"验证码\"}")
|
||||||
|
print(" 返回: {\"code\": 200, \"message\": \"验证成功\"}")
|
||||||
|
print("\n📍 检查Token: POST /check_token")
|
||||||
|
print(" 请求: {\"token\": \"xxx\"}")
|
||||||
|
print("📍 健康检查: GET /health")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
app.run(host='0.0.0.0', port=888, debug=True)
|
||||||
Reference in New Issue
Block a user