feat: add user creation form in admin panel

This commit is contained in:
2026-07-05 18:18:00 +07:00
parent 1ff473b6c6
commit 7f12e1d2ce
2 changed files with 74 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
from jinja2 import Environment, FileSystemLoader
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import func, select
from app.database import async_session_factory
@@ -13,6 +13,7 @@ from app.repositories.payment import PaymentRepository
from app.repositories.server import ServerRepository
from app.repositories.tariff import TariffRepository
from app.repositories.user import UserRepository
from app.services.user import UserService
router = APIRouter(prefix="/admin", tags=["admin"])
@@ -66,16 +67,49 @@ async def dashboard(request: Request):
@router.get("/users", response_class=HTMLResponse)
async def users_page(request: Request):
async def users_page(request: Request, error: str = ""):
async with async_session_factory() as session:
repo = UserRepository(session)
users = await repo.get_all()
return HTMLResponse(
_render("users.html", request=request, users=users)
_render("users.html", request=request, users=users, error=error)
)
@router.post("/users/create", response_class=HTMLResponse)
async def users_create(
request: Request,
telegram_id: int = Form(...),
username: str = Form(""),
full_name: str = Form(""),
language_code: str = Form("ru"),
):
async with async_session_factory() as session:
user_repo = UserRepository(session)
user_service = UserService(user_repo)
existing = await user_repo.get_by_telegram_id(telegram_id)
if existing is not None:
return HTMLResponse(
_render(
"users.html",
request=request,
users=await user_repo.get_all(),
error=f"User with telegram_id {telegram_id} already exists",
)
)
await user_service.register(
telegram_id=telegram_id,
username=username or None,
full_name=full_name or str(telegram_id),
language_code=language_code,
)
return RedirectResponse(url="/admin/users", status_code=303)
@router.get("/payments", response_class=HTMLResponse)
async def payments_page(request: Request):
async with async_session_factory() as session:

View File

@@ -2,6 +2,42 @@
{% block title %}Users — VPN Control Panel{% endblock %}
{% block content %}
<h2>Users</h2>
<div class="card" style="margin-bottom:20px;padding:16px;">
<form action="/admin/users/create" method="post" style="display:flex;gap:8px;align-items:end;flex-wrap:wrap;">
<div>
<label style="font-size:11px;color:#888;display:block;margin-bottom:2px;">Telegram ID</label>
<input type="number" name="telegram_id" required
style="padding:6px 10px;border:1px solid #ddd;border-radius:4px;font-size:13px;">
</div>
<div>
<label style="font-size:11px;color:#888;display:block;margin-bottom:2px;">Username</label>
<input type="text" name="username" placeholder="@username"
style="padding:6px 10px;border:1px solid #ddd;border-radius:4px;font-size:13px;">
</div>
<div>
<label style="font-size:11px;color:#888;display:block;margin-bottom:2px;">Full Name</label>
<input type="text" name="full_name" placeholder="Name"
style="padding:6px 10px;border:1px solid #ddd;border-radius:4px;font-size:13px;">
</div>
<div>
<label style="font-size:11px;color:#888;display:block;margin-bottom:2px;">Language</label>
<select name="language_code" style="padding:6px 10px;border:1px solid #ddd;border-radius:4px;font-size:13px;">
<option value="ru">RU</option>
<option value="en">EN</option>
</select>
</div>
<button type="submit"
style="padding:7px 16px;background:#1a1a2e;color:#fff;border:none;border-radius:4px;font-size:13px;cursor:pointer;">
+ Add User
</button>
</form>
</div>
{% if error %}
<div style="color:#c53030;font-size:13px;margin-bottom:12px;">{{ error }}</div>
{% endif %}
<div class="count">{{ users|length }} total</div>
<table>
<thead>