feat(private): initialize private API

This commit is contained in:
MingxuanGame
2025-08-11 14:41:07 +00:00
parent d8c607137a
commit 8acd4578e2
7 changed files with 65 additions and 1 deletions

View File

@@ -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

View File

@@ -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 设置
| 变量名 | 描述 | 默认值 |

View File

@@ -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

View File

@@ -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",
]

View File

@@ -0,0 +1,7 @@
from __future__ import annotations
from .router import router as private_router
__all__ = [
"private_router",
]

View 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,
)

View File

@@ -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