Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/service/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def _build_access_token(self, user: User) -> tuple[str, int]:
"iat": int(now.timestamp()),
"exp": int(expire_at.timestamp()),
"type": "access",
"is_admin": bool(user.is_admin),
}
token = jwt.encode(payload, self._settings.jwt_secret_key, algorithm=self._settings.jwt_algorithm)
return token, int(expire_delta.total_seconds())
Expand All @@ -60,6 +61,7 @@ def _build_refresh_token(self, user: User) -> str:
"iat": int(now.timestamp()),
"exp": int(expire_at.timestamp()),
"type": "refresh",
"is_admin": bool(user.is_admin),
}
return jwt.encode(payload, self._settings.jwt_secret_key, algorithm=self._settings.jwt_algorithm)

Expand Down
8 changes: 6 additions & 2 deletions app/web/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ def custom_template_response(request, name: str, context: dict = None, *args, **
payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm])
email = payload.get("email")
user_id = payload.get("sub")
is_admin = payload.get("is_admin", False)
if email and user_id:
class SimpleUser:
def __init__(self, uid, email):
def __init__(self, uid, email, is_admin):
self.id = uid
self.email = email
context["current_user"] = SimpleUser(int(user_id), email)
self.is_admin = is_admin
context["current_user"] = SimpleUser(int(user_id), email, is_admin)
context["user_id"] = int(user_id)
except Exception:
pass
Expand Down Expand Up @@ -103,6 +105,7 @@ def register_web_routes(app):
from app.web.routes.repository import router as repository_router
from app.web.routes.status import router as status_router
from app.web.routes.features import router as features_router
from app.web.routes.admin import router as admin_router

@app.get("/")
async def root(request: Request):
Expand All @@ -123,3 +126,4 @@ async def root(request: Request):
app.include_router(repository_router)
app.include_router(status_router)
app.include_router(features_router)
app.include_router(admin_router)
26 changes: 26 additions & 0 deletions app/web/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from fastapi import Depends, HTTPException, status, Request
from fastapi.responses import RedirectResponse
import jwt
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_db_session
from app.models.user import User
from app.config import get_settings
from app.common.exceptions import DemoRestrictionError

Expand Down Expand Up @@ -28,3 +31,26 @@ async def get_current_user_from_cookie(request: Request) -> int:
except Exception:
raise HTTPException(status_code=303, headers={"Location": "/web/login"})


async def get_current_admin_from_cookie(
request: Request,
db_session: AsyncSession = Depends(get_db_session)
) -> User:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
token = request.cookies.get("web_token")
if not token:
raise HTTPException(status_code=303, headers={"Location": "/web/login"})
try:
settings = get_settings()
payload = jwt.decode(token, settings.jwt_secret_key, algorithms=["HS256"])
user_id = int(payload["sub"])
user = await db_session.get(User, user_id)
if not user or not user.is_active:
raise HTTPException(status_code=303, headers={"Location": "/web/login"})
if not user.is_admin:
raise HTTPException(status_code=303, headers={"Location": "/web/dashboard"})
return user
except HTTPException:
raise
except Exception:
raise HTTPException(status_code=303, headers={"Location": "/web/login"})

46 changes: 43 additions & 3 deletions app/web/routes/accounts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from fastapi import APIRouter, Depends, Form, Request, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse, Response
from sqlalchemy import select
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db_session, get_telegram_service
Expand Down Expand Up @@ -30,6 +30,22 @@ async def list_accounts(
})


async def _auto_assign_proxy(db_session: AsyncSession) -> int:
"""自动分配一个使用最少的、活跃的代理 IP ID。"""
stmt = (
select(ProxyInfo.id)
.where(ProxyInfo.is_active == True)
.outerjoin(TelegramAccount, (ProxyInfo.id == TelegramAccount.proxy_id) & (TelegramAccount.is_active == True))
.group_by(ProxyInfo.id)
.order_by(func.count(TelegramAccount.id).asc())
.limit(1)
)
result = await db_session.scalar(stmt)
if result is None:
raise ValueError("系统暂无可用代理 IP,请联系管理员配置。")
return result


@router.get("/accounts/new", response_class=HTMLResponse)
async def new_account_page(
request: Request,
Expand All @@ -49,8 +65,20 @@ async def request_phone_code(
proxy_id: str = Form(""),
telegram_service: TelegramService = Depends(get_telegram_service),
user_id: int = Depends(get_current_user_from_cookie),
db_session: AsyncSession = Depends(get_db_session),
):
pid = int(proxy_id) if proxy_id and proxy_id.strip() else None
try:
pid = await _auto_assign_proxy(db_session)
except ValueError as e:
return HTMLResponse(f"""
<div class="bg-red-50 border-l-4 border-red-400 p-4 mb-4">
<p class="text-sm text-red-700">错误: {str(e)}</p>
</div>
<button onclick="window.location.reload()" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700">
重新开始
</button>
""")

try:
res = await telegram_service.RequestPhoneLoginCode(phone_number, proxy_id=pid)
account_id = res["account_id"]
Expand Down Expand Up @@ -160,8 +188,20 @@ async def login_with_session(
proxy_id: str = Form(""),
telegram_service: TelegramService = Depends(get_telegram_service),
user_id: int = Depends(get_current_user_from_cookie),
db_session: AsyncSession = Depends(get_db_session),
):
pid = int(proxy_id) if proxy_id and proxy_id.strip() else None
try:
pid = await _auto_assign_proxy(db_session)
except ValueError as e:
return HTMLResponse(f"""
<div class="bg-red-50 border-l-4 border-red-400 p-4 mb-4">
<p class="text-sm text-red-700">导入失败: {str(e)}</p>
</div>
<button onclick="window.location.reload()" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-600 hover:bg-gray-700">
重新开始
</button>
""")

try:
await telegram_service.CreateAccountWithSessionLogin(phone_number, session_string, proxy_id=pid)
response = Response()
Expand Down
Loading
Loading