3fe8a47e04
- Add 85 custom Dildo items (knife/skin pattern names, various rarities) - Create Dildo Case config (hidden, items obtained via contracts) - Fix admin user_detail.html nested script tag (Notify fallback removed) - Fix admin cases.html embedded script tag - Add notifications.js to admin base.html - Fix admin users.html: use counts instead of relationship lists - Replace old money format with |money filter across all templates - Standardize nav partial (_nav.html) with active_page highlighting - Fix RARITY_COLORS rename in activity sidebar - Fix toast animation (forwards) and remove duplicate CSS - Replace showToast() with Notify.* in upgrade page - Add cs2_simulator.db and *.log to .gitignore - Create remote-deploy.sh with DB exclusion
811 lines
28 KiB
Python
811 lines
28 KiB
Python
from fastapi import APIRouter, Request, Depends, Form, HTTPException
|
|
from fastapi.responses import HTMLResponse, JSONResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import desc, func
|
|
from typing import Optional
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from database import get_db, User, InventoryItem, CaseOpening, Contract
|
|
from auth import get_current_admin
|
|
from rpu import get_user_rpu, calculate_rpu_level, get_rpu_description
|
|
|
|
admin_router = APIRouter(prefix="/admin", tags=["admin"])
|
|
templates = Jinja2Templates(directory="templates")
|
|
|
|
def money_filter(value):
|
|
try:
|
|
return "{:,.0f}".format(float(value)).replace(",", " ")
|
|
except (ValueError, TypeError):
|
|
return "0"
|
|
|
|
templates.env.filters["money"] = money_filter
|
|
|
|
# Загрузка конфигов
|
|
CASES_FILE = Path("cases.json")
|
|
MAINPAGE_FILE = Path("mainpage.json")
|
|
|
|
def load_cases_config():
|
|
if CASES_FILE.exists():
|
|
with open(CASES_FILE, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
return []
|
|
|
|
def save_cases_config(cases):
|
|
with open(CASES_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(cases, f, ensure_ascii=False, indent=2)
|
|
|
|
def load_mainpage_config():
|
|
if MAINPAGE_FILE.exists():
|
|
with open(MAINPAGE_FILE, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
return []
|
|
|
|
def save_mainpage_config(config):
|
|
with open(MAINPAGE_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
|
def get_rpu_level_for_user(user_id: int) -> float:
|
|
"""Хелпер для шаблона"""
|
|
db = next(get_db())
|
|
rpu = get_user_rpu(db, user_id)
|
|
return calculate_rpu_level(rpu)
|
|
|
|
# ========== СТРАНИЦЫ ==========
|
|
|
|
@admin_router.get("/", response_class=HTMLResponse)
|
|
async def admin_dashboard(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Главная страница админки"""
|
|
|
|
# Основная статистика
|
|
total_users = db.query(User).count()
|
|
total_items = db.query(InventoryItem).count()
|
|
total_openings = db.query(CaseOpening).count()
|
|
total_contracts = db.query(Contract).count()
|
|
total_balance = db.query(func.sum(User.balance)).scalar() or 0
|
|
|
|
# Статистика РПУ
|
|
from rpu import UserRPU
|
|
all_rpu = db.query(UserRPU).all()
|
|
rpu_count = len(all_rpu)
|
|
|
|
avg_rpu = 50.0
|
|
lucky_count = 0
|
|
normal_count = 0
|
|
unlucky_count = 0
|
|
auto_adjust_count = 0
|
|
total_spent = 0.0
|
|
|
|
if rpu_count > 0:
|
|
for r in all_rpu:
|
|
level = calculate_rpu_level(r)
|
|
if level > 60:
|
|
lucky_count += 1
|
|
elif level < 40:
|
|
unlucky_count += 1
|
|
else:
|
|
normal_count += 1
|
|
|
|
if r.auto_adjust:
|
|
auto_adjust_count += 1
|
|
|
|
total_spent += r.total_spent
|
|
|
|
avg_rpu = sum(calculate_rpu_level(r) for r in all_rpu) / rpu_count
|
|
|
|
recent_users = db.query(User).order_by(desc(User.created_at)).limit(10).all()
|
|
|
|
return templates.TemplateResponse("admin/dashboard.html", {
|
|
"request": request,
|
|
"user": admin,
|
|
"total_users": total_users,
|
|
"total_items": total_items,
|
|
"total_openings": total_openings,
|
|
"total_contracts": total_contracts,
|
|
"total_balance": total_balance,
|
|
"recent_users": recent_users,
|
|
"rpu_count": rpu_count,
|
|
"avg_rpu": round(avg_rpu, 1),
|
|
"lucky_count": lucky_count,
|
|
"normal_count": normal_count,
|
|
"unlucky_count": unlucky_count,
|
|
"auto_adjust_count": auto_adjust_count,
|
|
"total_spent": total_spent,
|
|
"get_rpu_level": get_rpu_level_for_user
|
|
})
|
|
|
|
@admin_router.get("/users", response_class=HTMLResponse)
|
|
async def admin_users(
|
|
request: Request,
|
|
search: Optional[str] = None,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Список пользователей"""
|
|
|
|
query = db.query(User)
|
|
if search:
|
|
query = query.filter(User.username.contains(search))
|
|
|
|
users = query.order_by(desc(User.created_at)).limit(100).all()
|
|
|
|
# Добавляем уровень РПУ для каждого пользователя
|
|
users_with_rpu = []
|
|
for u in users:
|
|
rpu = get_user_rpu(db, u.id)
|
|
users_with_rpu.append({
|
|
"id": u.id,
|
|
"username": u.username,
|
|
"balance": u.balance,
|
|
"is_admin": u.is_admin,
|
|
"created_at": u.created_at,
|
|
"case_openings": db.query(CaseOpening).filter(CaseOpening.user_id == u.id).count(),
|
|
"contracts": db.query(Contract).filter(Contract.user_id == u.id).count(),
|
|
"inventory_count": db.query(InventoryItem).filter(InventoryItem.user_id == u.id).count(),
|
|
"rpu_level": round(calculate_rpu_level(rpu))
|
|
})
|
|
|
|
return templates.TemplateResponse("admin/users.html", {
|
|
"request": request,
|
|
"user": admin,
|
|
"users": users_with_rpu,
|
|
"search": search
|
|
})
|
|
|
|
@admin_router.get("/user/{user_id}", response_class=HTMLResponse)
|
|
async def admin_user_detail(
|
|
user_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
raise HTTPException(404, "Пользователь не найден")
|
|
|
|
total_items = db.query(InventoryItem).filter(InventoryItem.user_id == user_id).count()
|
|
total_openings = db.query(CaseOpening).filter(CaseOpening.user_id == user_id).count()
|
|
total_contracts = db.query(Contract).filter(Contract.user_id == user_id).count()
|
|
|
|
inventory = db.query(InventoryItem).filter(
|
|
InventoryItem.user_id == user_id
|
|
).order_by(desc(InventoryItem.obtained_at)).limit(50).all()
|
|
|
|
return templates.TemplateResponse("admin/user_detail.html", {
|
|
"request": request,
|
|
"user": admin,
|
|
"target_user": target_user,
|
|
"total_items": total_items,
|
|
"total_openings": total_openings,
|
|
"total_contracts": total_contracts,
|
|
"inventory": inventory
|
|
})
|
|
|
|
@admin_router.get("/cases", response_class=HTMLResponse)
|
|
async def admin_cases(
|
|
request: Request,
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
from frontend import ALL_ITEMS
|
|
|
|
cases = load_cases_config()
|
|
mainpage = load_mainpage_config()
|
|
|
|
for case in cases:
|
|
if not isinstance(case.get('items'), list):
|
|
case['items'] = []
|
|
|
|
all_items = []
|
|
for i, item in enumerate(ALL_ITEMS[:500]):
|
|
all_items.append({
|
|
"id": item.get("_id", i),
|
|
"name": item.get("market_hash_name", "Unknown"),
|
|
"rarity": item.get("rarity", "Unknown"),
|
|
"image_url": item.get("image_url", "")
|
|
})
|
|
|
|
return templates.TemplateResponse("admin/cases.html", {
|
|
"request": request,
|
|
"user": admin,
|
|
"cases": cases,
|
|
"mainpage": mainpage,
|
|
"all_items": all_items,
|
|
"cases_config": {c['name']: c for c in cases}
|
|
})
|
|
|
|
# ========== API ПОЛЬЗОВАТЕЛИ ==========
|
|
|
|
@admin_router.post("/api/user/{user_id}/balance")
|
|
async def admin_set_balance(
|
|
user_id: int,
|
|
amount: float = Form(...),
|
|
operation: str = Form(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
old_balance = target_user.balance
|
|
|
|
if operation == "add":
|
|
target_user.balance += amount
|
|
message = f"Добавлено {amount:.2f} ₽"
|
|
elif operation == "remove":
|
|
target_user.balance = max(0, target_user.balance - amount)
|
|
message = f"Снято {amount:.2f} ₽"
|
|
else:
|
|
target_user.balance = amount
|
|
message = f"Установлен баланс {amount:.2f} ₽"
|
|
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"old_balance": old_balance,
|
|
"new_balance": target_user.balance,
|
|
"message": message
|
|
})
|
|
|
|
@admin_router.post("/api/user/{user_id}/give-item")
|
|
async def admin_give_item(
|
|
user_id: int,
|
|
item_id: int = Form(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
from frontend import ALL_ITEMS, generate_item_float
|
|
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
item_data = None
|
|
for idx, item in enumerate(ALL_ITEMS):
|
|
if item.get("_id", idx) == item_id:
|
|
item_data = item
|
|
break
|
|
|
|
if not item_data:
|
|
return JSONResponse(status_code=404, content={"error": "Предмет не найден"})
|
|
|
|
item_float = generate_item_float(item_data.get("rarity", "Unknown"))
|
|
|
|
inventory_item = InventoryItem(
|
|
user_id=user_id,
|
|
item_id=item_id,
|
|
market_hash_name=item_data.get("market_hash_name", "Unknown"),
|
|
rarity=item_data.get("rarity", "Unknown"),
|
|
wear=item_data.get("wear", "Unknown"),
|
|
float_value=item_float,
|
|
type=item_data.get("type", "Normal"),
|
|
obtained_from="admin"
|
|
)
|
|
db.add(inventory_item)
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Предмет '{item_data.get('market_hash_name')}' выдан пользователю {target_user.username}"
|
|
})
|
|
|
|
@admin_router.post("/api/user/{user_id}/toggle-ban")
|
|
async def admin_toggle_ban(
|
|
user_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
target_user.is_banned = not target_user.is_banned
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"is_banned": target_user.is_banned,
|
|
"message": f"Пользователь {'забанен' if target_user.is_banned else 'разбанен'}"
|
|
})
|
|
|
|
@admin_router.post("/api/user/{user_id}/toggle-admin")
|
|
async def admin_toggle_admin(
|
|
user_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
if target_user.id == admin.id:
|
|
return JSONResponse(status_code=400, content={"error": "Нельзя изменить свой статус"})
|
|
|
|
target_user.is_admin = not target_user.is_admin
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"is_admin": target_user.is_admin,
|
|
"message": f"Статус администратора {'включен' if target_user.is_admin else 'выключен'}"
|
|
})
|
|
|
|
@admin_router.post("/api/user/{user_id}/delete-item/{inventory_id}")
|
|
async def admin_delete_item(
|
|
user_id: int,
|
|
inventory_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
item = db.query(InventoryItem).filter(
|
|
InventoryItem.id == inventory_id,
|
|
InventoryItem.user_id == user_id
|
|
).first()
|
|
|
|
if not item:
|
|
return JSONResponse(status_code=404, content={"error": "Предмет не найден"})
|
|
|
|
db.delete(item)
|
|
db.commit()
|
|
|
|
return JSONResponse({"success": True, "message": "Предмет удален"})
|
|
|
|
# ========== API РПУ ==========
|
|
|
|
@admin_router.get("/api/user/{user_id}/rpu")
|
|
async def admin_get_user_rpu(
|
|
user_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
rpu = get_user_rpu(db, user_id)
|
|
|
|
return {
|
|
"user_id": user_id,
|
|
"username": target_user.username,
|
|
"multipliers": {
|
|
"consumer": rpu.consumer_multiplier,
|
|
"industrial": rpu.industrial_multiplier,
|
|
"mil_spec": rpu.mil_spec_multiplier,
|
|
"restricted": rpu.restricted_multiplier,
|
|
"classified": rpu.classified_multiplier,
|
|
"covert": rpu.covert_multiplier,
|
|
"rare_special": rpu.rare_special_multiplier,
|
|
},
|
|
"luck_multiplier": rpu.luck_multiplier,
|
|
"auto_adjust": rpu.auto_adjust,
|
|
"rpu_level": calculate_rpu_level(rpu),
|
|
"stats": {
|
|
"total_spent": rpu.total_spent,
|
|
"total_opened": rpu.total_opened
|
|
}
|
|
}
|
|
|
|
@admin_router.post("/api/user/{user_id}/rpu/update")
|
|
async def admin_update_user_rpu(
|
|
user_id: int,
|
|
consumer: float = Form(1.0),
|
|
industrial: float = Form(1.0),
|
|
mil_spec: float = Form(1.0),
|
|
restricted: float = Form(1.0),
|
|
classified: float = Form(1.0),
|
|
covert: float = Form(1.0),
|
|
rare_special: float = Form(1.0),
|
|
luck: float = Form(1.0),
|
|
auto_adjust: bool = Form(False),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
rpu = get_user_rpu(db, user_id)
|
|
|
|
rpu.consumer_multiplier = max(0.1, min(3.0, consumer))
|
|
rpu.industrial_multiplier = max(0.1, min(3.0, industrial))
|
|
rpu.mil_spec_multiplier = max(0.1, min(3.0, mil_spec))
|
|
rpu.restricted_multiplier = max(0.1, min(3.0, restricted))
|
|
rpu.classified_multiplier = max(0.1, min(3.0, classified))
|
|
rpu.covert_multiplier = max(0.1, min(3.0, covert))
|
|
rpu.rare_special_multiplier = max(0.1, min(3.0, rare_special))
|
|
rpu.luck_multiplier = max(0.1, min(3.0, luck))
|
|
rpu.auto_adjust = auto_adjust
|
|
rpu.updated_at = datetime.utcnow()
|
|
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Настройки РПУ для {target_user.username} обновлены",
|
|
"rpu_level": calculate_rpu_level(rpu)
|
|
})
|
|
|
|
@admin_router.post("/api/user/{user_id}/rpu/preset")
|
|
async def admin_set_rpu_preset(
|
|
user_id: int,
|
|
preset: str = Form(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
# Теперь: низкий множитель = высокий РПУ (не везёт)
|
|
# высокий множитель = низкий РПУ (везёт)
|
|
presets = {
|
|
"lucky": {"all": 2.0, "luck": 1.8}, # ~10% РПУ - очень везёт
|
|
"normal": {"all": 1.0, "luck": 1.0}, # ~50% РПУ - обычно
|
|
"unlucky": {"all": 0.6, "luck": 0.7}, # ~75% РПУ - не везёт
|
|
"very_unlucky": {"all": 0.3, "luck": 0.4} # ~95% РПУ - проклят
|
|
}
|
|
|
|
if preset not in presets:
|
|
return JSONResponse(status_code=400, content={"error": "Неизвестный пресет"})
|
|
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
rpu = get_user_rpu(db, user_id)
|
|
p = presets[preset]
|
|
|
|
rpu.consumer_multiplier = p["all"]
|
|
rpu.industrial_multiplier = p["all"]
|
|
rpu.mil_spec_multiplier = p["all"]
|
|
rpu.restricted_multiplier = p["all"]
|
|
rpu.classified_multiplier = p["all"]
|
|
rpu.covert_multiplier = p["all"]
|
|
rpu.rare_special_multiplier = p["all"]
|
|
rpu.luck_multiplier = p["luck"]
|
|
rpu.updated_at = datetime.utcnow()
|
|
|
|
db.commit()
|
|
|
|
level = calculate_rpu_level(rpu)
|
|
desc = get_rpu_description(level)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Пресет '{preset}' применен. Уровень РПУ: {level:.0f}% ({desc})"
|
|
})
|
|
|
|
# ========== API КЕЙСЫ ==========
|
|
|
|
@admin_router.post("/api/cases/create")
|
|
async def admin_create_case(
|
|
name: str = Form(...),
|
|
display_name: str = Form(...),
|
|
price: float = Form(...),
|
|
description: str = Form(""),
|
|
image_url: str = Form(""),
|
|
items: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
cases = load_cases_config()
|
|
|
|
for case in cases:
|
|
if case['name'] == name:
|
|
return JSONResponse(status_code=400, content={"error": "Кейс с таким именем уже существует"})
|
|
|
|
try:
|
|
item_ids = json.loads(items)
|
|
except:
|
|
return JSONResponse(status_code=400, content={"error": "Неверный формат списка предметов"})
|
|
|
|
new_case = {
|
|
"name": name,
|
|
"display_name": display_name,
|
|
"price_open": price,
|
|
"items": item_ids,
|
|
"image_url": image_url,
|
|
"description": description
|
|
}
|
|
|
|
cases.append(new_case)
|
|
save_cases_config(cases)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"case": new_case,
|
|
"message": f"Кейс '{display_name}' создан"
|
|
})
|
|
|
|
@admin_router.post("/api/cases/update/{case_name}")
|
|
async def admin_update_case(
|
|
case_name: str,
|
|
display_name: str = Form(...),
|
|
price: float = Form(...),
|
|
description: str = Form(""),
|
|
image_url: str = Form(""),
|
|
items: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
cases = load_cases_config()
|
|
|
|
for case in cases:
|
|
if case['name'] == case_name:
|
|
try:
|
|
item_ids = json.loads(items)
|
|
except:
|
|
return JSONResponse(status_code=400, content={"error": "Неверный формат списка предметов"})
|
|
|
|
case['display_name'] = display_name
|
|
case['price_open'] = price
|
|
case['description'] = description
|
|
case['image_url'] = image_url
|
|
case['items'] = item_ids
|
|
|
|
save_cases_config(cases)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"case": case,
|
|
"message": f"Кейс '{display_name}' обновлен"
|
|
})
|
|
|
|
return JSONResponse(status_code=404, content={"error": "Кейс не найден"})
|
|
|
|
@admin_router.post("/api/cases/delete/{case_name}")
|
|
async def admin_delete_case(
|
|
case_name: str,
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
cases = load_cases_config()
|
|
cases = [c for c in cases if c['name'] != case_name]
|
|
save_cases_config(cases)
|
|
|
|
mainpage = load_mainpage_config()
|
|
for section in mainpage:
|
|
section['cases'] = [c for c in section['cases'] if c != case_name]
|
|
save_mainpage_config(mainpage)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Кейс '{case_name}' удален"
|
|
})
|
|
|
|
@admin_router.post("/api/mainpage/update")
|
|
async def admin_update_mainpage(
|
|
config: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
try:
|
|
new_config = json.loads(config)
|
|
except:
|
|
return JSONResponse(status_code=400, content={"error": "Неверный формат JSON"})
|
|
|
|
save_mainpage_config(new_config)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": "Конфигурация главной страницы обновлена"
|
|
})
|
|
|
|
@admin_router.get("/api/items/search")
|
|
async def admin_search_items(
|
|
q: str = "",
|
|
min_price: float = 0,
|
|
max_price: float = 0,
|
|
limit: int = 200
|
|
):
|
|
"""Поиск предметов для добавления в кейс / апгрейда"""
|
|
from frontend import ALL_ITEMS
|
|
|
|
query = q.lower().strip()
|
|
|
|
results = []
|
|
|
|
def accept_item(item, idx):
|
|
if min_price > 0 and item.get("price_rub", 0) < min_price:
|
|
return False
|
|
if max_price > 0 and item.get("price_rub", 0) > max_price:
|
|
return False
|
|
return True
|
|
|
|
if not query or len(query) < 2:
|
|
# Без поиска возвращаем разнообразные предметы со всего ассортимента
|
|
# Если есть ценовой фильтр — проходим по всем, иначе шагами
|
|
if min_price > 0 or max_price > 0:
|
|
for idx, item in enumerate(ALL_ITEMS):
|
|
if accept_item(item, idx):
|
|
item_id = item.get("_id", idx)
|
|
results.append({
|
|
"id": item_id,
|
|
"name": item.get("market_hash_name", "Unknown"),
|
|
"rarity": item.get("rarity", "Unknown"),
|
|
"image_url": item.get("image_url", ""),
|
|
"price_rub": item.get("price_rub", 0),
|
|
"weapon": item.get("weapon", ""),
|
|
"pattern": item.get("pattern", "")
|
|
})
|
|
if len(results) >= limit:
|
|
break
|
|
else:
|
|
step = max(1, len(ALL_ITEMS) // limit)
|
|
for idx in range(0, len(ALL_ITEMS), step):
|
|
if not accept_item(ALL_ITEMS[idx], idx):
|
|
continue
|
|
item = ALL_ITEMS[idx]
|
|
item_id = item.get("_id", idx)
|
|
results.append({
|
|
"id": item_id,
|
|
"name": item.get("market_hash_name", "Unknown"),
|
|
"rarity": item.get("rarity", "Unknown"),
|
|
"image_url": item.get("image_url", ""),
|
|
"price_rub": item.get("price_rub", 0),
|
|
"weapon": item.get("weapon", ""),
|
|
"pattern": item.get("pattern", "")
|
|
})
|
|
if len(results) >= 200:
|
|
break
|
|
else:
|
|
for idx, item in enumerate(ALL_ITEMS):
|
|
if not accept_item(item, idx):
|
|
continue
|
|
name = item.get("market_hash_name", "").lower()
|
|
weapon = item.get("weapon", "").lower()
|
|
pattern = item.get("pattern", "").lower()
|
|
|
|
if query in name or query in weapon or query in pattern:
|
|
item_id = item.get("_id", idx)
|
|
results.append({
|
|
"id": item_id,
|
|
"name": item.get("market_hash_name", "Unknown"),
|
|
"rarity": item.get("rarity", "Unknown"),
|
|
"image_url": item.get("image_url", ""),
|
|
"price_rub": item.get("price_rub", 0),
|
|
"weapon": item.get("weapon", ""),
|
|
"pattern": item.get("pattern", "")
|
|
})
|
|
if len(results) >= 200:
|
|
break
|
|
|
|
return results[:200]
|
|
|
|
|
|
@admin_router.post("/api/mainpage/section/add")
|
|
async def admin_add_mainpage_section(
|
|
tab: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Добавить новую секцию на главную"""
|
|
mainpage = load_mainpage_config()
|
|
|
|
# Проверяем, нет ли уже такой секции
|
|
for section in mainpage:
|
|
if section['tab'] == tab:
|
|
return JSONResponse(status_code=400, content={"error": "Секция уже существует"})
|
|
|
|
mainpage.append({
|
|
"tab": tab,
|
|
"cases": []
|
|
})
|
|
save_mainpage_config(mainpage)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Секция '{tab}' добавлена"
|
|
})
|
|
|
|
@admin_router.post("/api/mainpage/section/delete")
|
|
async def admin_delete_mainpage_section(
|
|
tab: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Удалить секцию с главной"""
|
|
mainpage = load_mainpage_config()
|
|
mainpage = [s for s in mainpage if s['tab'] != tab]
|
|
save_mainpage_config(mainpage)
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Секция '{tab}' удалена"
|
|
})
|
|
|
|
@admin_router.post("/api/mainpage/section/add-case")
|
|
async def admin_add_case_to_section(
|
|
tab: str = Form(...),
|
|
case_name: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Добавить кейс в секцию"""
|
|
mainpage = load_mainpage_config()
|
|
|
|
for section in mainpage:
|
|
if section['tab'] == tab:
|
|
if case_name not in section['cases']:
|
|
section['cases'].append(case_name)
|
|
save_mainpage_config(mainpage)
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Кейс '{case_name}' добавлен в секцию '{tab}'"
|
|
})
|
|
else:
|
|
return JSONResponse(status_code=400, content={"error": "Кейс уже в секции"})
|
|
|
|
return JSONResponse(status_code=404, content={"error": "Секция не найдена"})
|
|
|
|
@admin_router.post("/api/mainpage/section/remove-case")
|
|
async def admin_remove_case_from_section(
|
|
tab: str = Form(...),
|
|
case_name: str = Form(...),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Удалить кейс из секции"""
|
|
mainpage = load_mainpage_config()
|
|
|
|
for section in mainpage:
|
|
if section['tab'] == tab:
|
|
if case_name in section['cases']:
|
|
section['cases'].remove(case_name)
|
|
save_mainpage_config(mainpage)
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Кейс '{case_name}' удален из секции '{tab}'"
|
|
})
|
|
|
|
return JSONResponse(status_code=404, content={"error": "Секция или кейс не найдены"})
|
|
|
|
@admin_router.get("/api/cases/list")
|
|
async def admin_get_cases_list(
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Получить список всех кейсов для выбора"""
|
|
cases = load_cases_config()
|
|
return [{"name": c['name'], "display_name": c.get('display_name', c['name'])} for c in cases]
|
|
|
|
@admin_router.get("/api/items/{item_id}")
|
|
async def admin_get_item(
|
|
item_id: int,
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Получить информацию о предмете по ID"""
|
|
from frontend import ALL_ITEMS
|
|
|
|
for idx, item in enumerate(ALL_ITEMS):
|
|
if item.get("_id", idx) == item_id:
|
|
return {
|
|
"id": item_id,
|
|
"name": item.get("market_hash_name", "Unknown"),
|
|
"rarity": item.get("rarity", "Unknown"),
|
|
"image_url": item.get("image_url", ""),
|
|
"price_rub": item.get("price_rub", 0),
|
|
"weapon": item.get("weapon", ""),
|
|
"pattern": item.get("pattern", ""),
|
|
"type": item.get("type", "Normal"),
|
|
"wear": item.get("wear", "")
|
|
}
|
|
|
|
return JSONResponse(status_code=404, content={"error": "Предмет не найден"})
|
|
|
|
|
|
@admin_router.post("/api/reset-password/{user_id}")
|
|
async def admin_reset_password(
|
|
user_id: int,
|
|
new_password: str = Form(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_current_admin)
|
|
):
|
|
"""Сброс пароля пользователя (только для админов)"""
|
|
from auth import get_password_hash
|
|
|
|
target_user = db.query(User).filter(User.id == user_id).first()
|
|
if not target_user:
|
|
return JSONResponse(status_code=404, content={"error": "Пользователь не найден"})
|
|
|
|
target_user.hashed_password = get_password_hash(new_password)
|
|
db.commit()
|
|
|
|
return JSONResponse({
|
|
"success": True,
|
|
"message": f"Пароль для {target_user.username} изменен"
|
|
}) |