diff --git a/app/admin/router.py b/app/admin/router.py index ca95d34..eea10ce 100644 --- a/app/admin/router.py +++ b/app/admin/router.py @@ -1,4 +1,5 @@ from jinja2 import Environment, FileSystemLoader +from loguru import logger from fastapi import APIRouter, Form, Request from fastapi.responses import HTMLResponse, RedirectResponse @@ -6,13 +7,15 @@ from sqlalchemy import func, select from app.database import async_session_factory from app.models.payment import Payment, PaymentStatus -from app.models.server import Server +from app.models.server import Server, ServerProtocol from app.models.tariff import Tariff from app.models.user import User 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.payment import PaymentService +from app.services.server import ServerService from app.services.user import UserService router = APIRouter(prefix="/admin", tags=["admin"]) @@ -110,20 +113,27 @@ async def users_create( return RedirectResponse(url="/admin/users", status_code=303) +async def _get_payments_with_tariffs(session): + stmt = select(Payment).order_by(Payment.created_at.desc()) + result = await session.execute(stmt) + payments = list(result.scalars().all()) + tariff_repo = TariffRepository(session) + tariffs_map = {} + for p in payments: + if p.tariff_id not in tariffs_map: + t = await tariff_repo.get(p.tariff_id) + tariffs_map[p.tariff_id] = t.name if t else "—" + return payments, tariffs_map + + @router.get("/payments", response_class=HTMLResponse) async def payments_page(request: Request): async with async_session_factory() as session: + user_repo = UserRepository(session) tariff_repo = TariffRepository(session) - - stmt = select(Payment).order_by(Payment.created_at.desc()) - result = await session.execute(stmt) - payments = list(result.scalars().all()) - - tariffs_map = {} - for p in payments: - if p.tariff_id not in tariffs_map: - t = await tariff_repo.get(p.tariff_id) - tariffs_map[p.tariff_id] = t.name if t else "—" + payments, tariffs_map = await _get_payments_with_tariffs(session) + users = await user_repo.get_all() + tariffs = await tariff_repo.get_all() return HTMLResponse( _render( @@ -131,10 +141,53 @@ async def payments_page(request: Request): request=request, payments=payments, tariffs_map=tariffs_map, + users=users, + tariffs=tariffs, ) ) +@router.post("/payments/create", response_class=HTMLResponse) +async def payments_create( + request: Request, + user_id: int = Form(...), + tariff_id: int = Form(...), + amount: float = Form(...), + currency: str = Form("RUB"), + provider: str = Form("manual"), +): + async with async_session_factory() as session: + payment_repo = PaymentRepository(session) + user_repo = UserRepository(session) + tariff_repo = TariffRepository(session) + payment_service = PaymentService(payment_repo, user_repo, tariff_repo) + + try: + payment = await payment_service.create_payment( + user_id=user_id, + tariff_id=tariff_id, + amount=amount, + currency=currency, + provider=provider, + ) + logger.info("Admin created payment id={}", payment.id) + except ValueError as e: + payments, tariffs_map = await _get_payments_with_tariffs(session) + return HTMLResponse( + _render( + "payments.html", + request=request, + payments=payments, + tariffs_map=tariffs_map, + users=await user_repo.get_all(), + tariffs=await tariff_repo.get_all(), + error=str(e), + ) + ) + + return RedirectResponse(url="/admin/payments", status_code=303) + + @router.get("/servers", response_class=HTMLResponse) async def servers_page(request: Request): async with async_session_factory() as session: @@ -142,10 +195,56 @@ async def servers_page(request: Request): servers = await repo.get_all() return HTMLResponse( - _render("servers.html", request=request, servers=servers) + _render( + "servers.html", + request=request, + servers=servers, + protocols=[p.value for p in ServerProtocol], + ) ) +@router.post("/servers/create", response_class=HTMLResponse) +async def servers_create( + request: Request, + name: str = Form(...), + host: str = Form(...), + port: int = Form(...), + protocol: str = Form(...), + location: str = Form(...), + country_code: str = Form(...), + max_users: int = Form(100), +): + async with async_session_factory() as session: + server_repo = ServerRepository(session) + server_service = ServerService(server_repo) + + try: + server = await server_service.create( + name=name, + host=host, + port=port, + protocol=ServerProtocol(protocol), + location=location, + country_code=country_code, + max_users=max_users, + ) + logger.info("Admin created server id={}", server.id) + except ValueError as e: + repo = ServerRepository(session) + return HTMLResponse( + _render( + "servers.html", + request=request, + servers=await repo.get_all(), + protocols=[p.value for p in ServerProtocol], + error=str(e), + ) + ) + + return RedirectResponse(url="/admin/servers", status_code=303) + + @router.get("/tariffs", response_class=HTMLResponse) async def tariffs_page(request: Request): async with async_session_factory() as session: @@ -155,3 +254,29 @@ async def tariffs_page(request: Request): return HTMLResponse( _render("tariffs.html", request=request, tariffs=tariffs) ) + + +@router.post("/tariffs/create", response_class=HTMLResponse) +async def tariffs_create( + request: Request, + name: str = Form(...), + duration_days: int = Form(...), + price: float = Form(...), + currency: str = Form("RUB"), + max_devices: int = Form(1), + traffic_gb: int = Form(0), +): + async with async_session_factory() as session: + tariff_repo = TariffRepository(session) + traffic = traffic_gb if traffic_gb > 0 else None + tariff = await tariff_repo.create( + name=name, + duration_days=duration_days, + price=price, + currency=currency, + max_devices=max_devices, + traffic_gb=traffic, + ) + logger.info("Admin created tariff id={}", tariff.id) + + return RedirectResponse(url="/admin/tariffs", status_code=303) diff --git a/app/templates/payments.html b/app/templates/payments.html index 092fe15..74c4784 100644 --- a/app/templates/payments.html +++ b/app/templates/payments.html @@ -2,6 +2,39 @@ {% block title %}Payments — VPN Control Panel{% endblock %} {% block content %}

Payments

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +{% if error %}
{{ error }}
{% endif %} +
{{ payments|length }} total
diff --git a/app/templates/servers.html b/app/templates/servers.html index 7488f3a..1fc052c 100644 --- a/app/templates/servers.html +++ b/app/templates/servers.html @@ -2,6 +2,43 @@ {% block title %}Servers — VPN Control Panel{% endblock %} {% block content %}

Servers

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ +{% if error %}
{{ error }}
{% endif %} +
{{ servers|length }} total
diff --git a/app/templates/tariffs.html b/app/templates/tariffs.html index eaf7d9f..5c3851a 100644 --- a/app/templates/tariffs.html +++ b/app/templates/tariffs.html @@ -2,6 +2,40 @@ {% block title %}Tariffs — VPN Control Panel{% endblock %} {% block content %}

Tariffs

+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
{{ tariffs|length }} total