feat(private): initialize private API
This commit is contained in:
@@ -20,6 +20,8 @@ HOST="0.0.0.0"
|
||||
PORT=8000
|
||||
# 调试模式,生产环境请设置为 false
|
||||
DEBUG=false
|
||||
# 私有 API 密钥,用于前后端 API 调用,使用 openssl rand -hex 32 生成
|
||||
PRIVATE_API_SECRET="your_private_api_secret_here"
|
||||
|
||||
# osu! 登录设置
|
||||
OSU_CLIENT_ID=5 # lazer client ID
|
||||
|
||||
@@ -67,6 +67,7 @@ docker-compose -f docker-compose-osurx.yml up -d
|
||||
| `HOST` | 服务器监听地址 | `0.0.0.0` |
|
||||
| `PORT` | 服务器监听端口 | `8000` |
|
||||
| `DEBUG` | 调试模式 | `false` |
|
||||
| `PRIVATE_API_SECRET` | 私有 API 密钥,用于前后端 API 调用 | `your_private_api_secret_here` |
|
||||
|
||||
### OAuth 设置
|
||||
| 变量名 | 描述 | 默认值 |
|
||||
|
||||
@@ -37,6 +37,7 @@ class Settings(BaseSettings):
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
debug: bool = False
|
||||
private_api_secret: str = "your_private_api_secret_here"
|
||||
|
||||
# SignalR 设置
|
||||
signalr_negotiate_timeout: int = 30
|
||||
|
||||
@@ -4,6 +4,13 @@ from app.signalr import signalr_router as signalr_router
|
||||
|
||||
from .auth import router as auth_router
|
||||
from .fetcher import fetcher_router as fetcher_router
|
||||
from .private import private_router as private_router
|
||||
from .v2 import api_v2_router as api_v2_router
|
||||
|
||||
__all__ = ["api_v2_router", "auth_router", "fetcher_router", "signalr_router"]
|
||||
__all__ = [
|
||||
"api_v2_router",
|
||||
"auth_router",
|
||||
"fetcher_router",
|
||||
"private_router",
|
||||
"signalr_router",
|
||||
]
|
||||
|
||||
7
app/router/private/__init__.py
Normal file
7
app/router/private/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .router import router as private_router
|
||||
|
||||
__all__ = [
|
||||
"private_router",
|
||||
]
|
||||
39
app/router/private/router.py
Normal file
39
app/router/private/router.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
|
||||
from app.config import settings
|
||||
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, Request
|
||||
|
||||
|
||||
async def verify_signature(
|
||||
request: Request,
|
||||
ts: int = Header(..., alias="X-Timestamp"),
|
||||
nonce: str = Header(..., alias="X-Nonce"),
|
||||
signature: str = Header(..., alias="X-Signature"),
|
||||
):
|
||||
path = request.url.path
|
||||
data = await request.body()
|
||||
body = data.decode("utf-8")
|
||||
|
||||
py_ts = ts // 1000
|
||||
if abs(time.time() - py_ts) > 30:
|
||||
raise HTTPException(status_code=403, detail="Invalid timestamp")
|
||||
|
||||
payload = f"{path}|{body}|{ts}|{nonce}"
|
||||
expected_sig = hmac.new(
|
||||
settings.private_api_secret.encode(), payload.encode(), hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
if not hmac.compare_digest(expected_sig, signature):
|
||||
raise HTTPException(status_code=403, detail="Invalid signature")
|
||||
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/private",
|
||||
dependencies=[Depends(verify_signature)],
|
||||
include_in_schema=False,
|
||||
)
|
||||
7
main.py
7
main.py
@@ -12,6 +12,7 @@ from app.router import (
|
||||
api_v2_router,
|
||||
auth_router,
|
||||
fetcher_router,
|
||||
private_router,
|
||||
signalr_router,
|
||||
)
|
||||
from app.service.daily_challenge import daily_challenge_job
|
||||
@@ -39,6 +40,7 @@ app.include_router(api_v2_router)
|
||||
app.include_router(signalr_router)
|
||||
app.include_router(fetcher_router)
|
||||
app.include_router(auth_router)
|
||||
app.include_router(private_router)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -63,6 +65,11 @@ if settings.osu_web_client_secret == "your_osu_web_client_secret_here":
|
||||
"osu_web_client_secret is unset. Your server is unsafe. "
|
||||
"Use this command to generate: openssl rand -hex 40"
|
||||
)
|
||||
if settings.private_api_secret == "your_private_api_secret_here":
|
||||
logger.warning(
|
||||
"private_api_secret is unset. Your server is unsafe. "
|
||||
"Use this command to generate: openssl rand -hex 32"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
Reference in New Issue
Block a user