diff --git a/web/app.py b/web/app.py index 0243244..56895b0 100644 --- a/web/app.py +++ b/web/app.py @@ -87,6 +87,13 @@ async def users_page(request: Request, search: str = "", filter: str = "", usern stmt = stmt.where(User.verified == False) elif filter == 'active_unverified': stmt = stmt.where(User.verified == False, User.message_count >= 50) + elif filter == 'ig': + # Только ИГ + from database.models import InitiativeGroup + ig_stmt = select(InitiativeGroup.user_id).where(InitiativeGroup.is_active == True) + ig_result = await session.execute(ig_stmt) + ig_user_ids = [row[0] for row in ig_result.all()] + stmt = stmt.where(User.user_id.in_(ig_user_ids)) # filter == 'all' или пустой — показываем всех # Поиск @@ -114,10 +121,11 @@ async def users_page(request: Request, search: str = "", filter: str = "", usern active_unverified = (await session.execute( select(func.count(User.user_id)).where(User.verified == False, User.message_count >= 50) )).scalar() + ig_count = len(ig_user_ids) return templates.TemplateResponse("users.html", {"request": request, "username": username, "users": users, "search": search, "current_filter": filter, - "stats": {"total": total_count, "verified": verified_count, "unverified": unverified_count, "active_unverified": active_unverified}, + "stats": {"total": total_count, "verified": verified_count, "unverified": unverified_count, "active_unverified": active_unverified, "ig": ig_count}, "ig_user_ids": ig_user_ids}) @@ -592,6 +600,22 @@ async def api_send_broadcast( chat_sent = True except Exception as e: logger.error(f"Ошибка отправки в чат: {e}") + + # Логируем в базу + try: + from sqlalchemy import text + async with AsyncSessionLocal() as session: + await session.execute(text(''' + INSERT INTO activity_log (user_id, action_type, action_data, recipients_count) + VALUES (:user_id, 'broadcast', :data, :count) + '''), { + "user_id": config.ADMIN_USER_ID, + "data": f"recipients={recipients}, text_len={len(text)}", + "count": success_count + }) + await session.commit() + except Exception as e: + logger.error(f"Ошибка логирования рассылки: {e}") return JSONResponse({ "success": True, @@ -605,6 +629,44 @@ async def api_send_broadcast( }) +# ============================================================================ +# ЛОГИРОВАНИЕ АКТИВНОСТИ +# ============================================================================ + +@app.get("/api/activity_log") +async def api_get_activity_log( + username: str = Depends(get_current_admin), + action_type: str = "", + days: int = 30 +): + """Получить логи активности""" + from sqlalchemy import text, desc + + async with AsyncSessionLocal() as session: + stmt = text(''' + SELECT * FROM activity_log + WHERE (:action_type = '' OR action_type = :action_type) + AND created_at >= datetime('now', '-' || :days || ' days') + ORDER BY created_at DESC + LIMIT 100 + ''') + + result = await session.execute(stmt, {"action_type": action_type, "days": str(days)}) + logs = [ + { + "id": row[0], + "user_id": row[1], + "action_type": row[2], + "action_data": row[3], + "recipients_count": row[4], + "created_at": row[5] + } + for row in result.all() + ] + + return JSONResponse({"success": True, "logs": logs}) + + # ============================================================================ # ОПРОСЫ # ============================================================================ @@ -744,22 +806,9 @@ async def api_export_json(username: str = Depends(get_current_admin)): # ============================================================================ -# НАСТРОЙКИ +# НАСТРОЙКИ (перенесено в /broadcast) # ============================================================================ - -@app.get("/settings", response_class=HTMLResponse) -async def settings_page(request: Request, username: str = Depends(get_current_admin)): - """Страница настроек""" - async with AsyncSessionLocal() as session: - from sqlalchemy import text - result = await session.execute(text("SELECT key, value FROM settings")) - settings = {row[0]: row[1] for row in result.all()} - - return templates.TemplateResponse("settings.html", { - "request": request, - "username": username, - "settings": settings - }) +# Настройка лимита рассылки ИГ теперь в /broadcast @app.post("/api/settings/ig_broadcast_limit") @@ -781,19 +830,24 @@ async def api_set_ig_broadcast_limit( return JSONResponse({"success": True, "message": f"Лимит установлен: {limit} сек"}) -@app.get("/settings", response_class=HTMLResponse) -async def settings_page(request: Request, username: str = Depends(get_current_admin)): - """Настройки""" - return templates.TemplateResponse("settings.html", { - "request": request, "username": username, - "config": { - "bot_token": config.BOT_TOKEN[:20] + "...", - "admin_chat_id": config.ADMIN_CHAT_ID, - "admin_user_id": config.ADMIN_USER_ID, - "use_proxy": config.USE_PROXY, - "web_login": config.WEB_ADMIN_LOGIN - } - }) + +@app.get("/api/settings/ig_broadcast_limit") +async def api_get_ig_broadcast_limit( + username: str = Depends(get_current_admin) +): + """Получить лимит рассылки ИГ""" + from sqlalchemy import text + + async with AsyncSessionLocal() as session: + result = await session.execute(text(''' + SELECT value FROM settings WHERE key = 'ig_broadcast_limit' + ''')) + row = result.first() + + if row: + return JSONResponse({"success": True, "value": row[0]}) + else: + return JSONResponse({"success": True, "value": "0"}) def run_web_server(host: str = "0.0.0.0", port: int = 8000): diff --git a/web/templates/broadcast.html b/web/templates/broadcast.html index 799e8cb..87fa338 100644 --- a/web/templates/broadcast.html +++ b/web/templates/broadcast.html @@ -91,17 +91,85 @@