PostgreSQL + async refactoring, WebSocket server, Redis caching, minified assets

This commit is contained in:
2026-07-25 13:31:40 +00:00
parent 47c7f17c90
commit 17da0b6d79
28 changed files with 1840 additions and 3244 deletions
+77 -19
View File
@@ -29,30 +29,29 @@ from pathlib import Path
import json
from pathlib import Path
from database import get_db, SessionLocal, User, InventoryItem, CaseOpening, Contract, Upgrade, UpgradeRPU, PromoCode
from database import get_db, get_db_executor, get_async_db, AsyncSessionLocal, SessionLocal, User, InventoryItem, CaseOpening, Contract, Upgrade, UpgradeRPU, PromoCode
from websocket_manager import manager, crash_game
from sqlalchemy import select, func as sa_func, desc
from achievements import (
seed_achievements, check_achievement,
check_open_cases_achievements, check_contracts_achievements,
check_upgrade_achievements, check_upgrade_risk_achievements, check_upgrade_result_achievements,
check_rare_item_achievement, check_knife_achievement,
check_spent_achievements,
check_balance_achievements,
check_slot_match_achievements, check_slot_plays_achievements,
check_slot_match_count_achievements,
check_inventory_achievements,
check_dildo_achievements, check_dildo_contract_achievement,
check_crash_achievements, check_crash_cashout_achievements,
check_items_unboxed_achievements, check_total_profit_achievements,
check_achievements_count_achievements,
check_upgrade_achievements, check_upgrade_risk_achievements,
check_upgrade_result_achievements, check_rare_item_achievement,
check_knife_achievement, check_spent_achievements,
check_balance_achievements, check_slot_match_achievements,
check_slot_plays_achievements, check_slot_match_count_achievements,
check_inventory_achievements, check_dildo_achievements,
check_dildo_contract_achievement, check_crash_achievements,
check_crash_cashout_achievements, check_items_unboxed_achievements,
check_total_profit_achievements, check_achievements_count_achievements,
check_rare_item_count_achievements, check_knife_count_achievements,
check_midnight_case_achievement, check_exact_cases_achievement,
check_collection_achievements,
get_user_achievements
check_collection_achievements, get_user_achievements
)
from auth import (
create_user, authenticate_user, create_access_token,
get_current_user, get_current_user_optional, get_password_hash,
get_current_user_sync, get_current_user_optional_sync,
ACCESS_TOKEN_EXPIRE_MINUTES, is_item_craftable
)
@@ -70,9 +69,10 @@ from backend import (
from upgrade import (
get_upgrade_rpu, calculate_upgrade_probability,
get_adjusted_upgrade_probability, spin_upgrade_wheel,
update_upgrade_stats
update_upgrade_stats,
get_upgrade_rpu_async, get_adjusted_upgrade_probability_async,
update_upgrade_stats_async
)
from rpu import get_adjusted_weights, get_user_rpu, update_rpu_stats, RARITY_TO_FIELD
from auth import get_password_hash
@@ -100,6 +100,40 @@ from backend import app as api_app
# Монтируем оригинальное API на /api/simulator чтобы избежать конфликтов
app.mount("/api/simulator", api_app)
# ─── Pagination helper ─────────────────────────────────────────────────────
DEFAULT_PAGE_SIZE = 50
def paginate(query, page: int = 1, page_size: int = DEFAULT_PAGE_SIZE):
offset = (page - 1) * page_size
return query.offset(offset).limit(page_size)
def paginated_response(items, total: int, page: int, page_size: int = DEFAULT_PAGE_SIZE):
return {
"items": items,
"total": total,
"page": page,
"page_size": page_size,
"pages": max(1, (total + page_size - 1) // page_size),
}
# ─── Rate Limiting ──────────────────────────────────────────────────────────
from collections import defaultdict
import time
_rate_limit_store: dict = defaultdict(list)
RATE_LIMIT_WINDOW = 60 # seconds
RATE_LIMIT_MAX_CASES = 30 # max case opens per window
RATE_LIMIT_MAX_GENERAL = 120 # max general requests per window
async def rate_limit_check(user_id: int, action: str, max_req: int, window: int = RATE_LIMIT_WINDOW):
key = f"{user_id}:{action}"
now = time.time()
_rate_limit_store[key] = [t for t in _rate_limit_store.get(key, []) if now - t < window]
if len(_rate_limit_store[key]) >= max_req:
return False
_rate_limit_store[key].append(now)
return True
# ─── Вспомогательные функции ───────────────────────────────────────────────
# Кэш dildo-предметов (создаётся один раз)
@@ -243,9 +277,33 @@ def record_activity_own_session(user_id: int, username: str, activity_type: str,
}
# Сидируем достижения при старте
db_seed = next(get_db())
seed_achievements(db_seed)
db_seed.close()
try:
from database import init_sync_db
init_sync_db()
db_seed = next(get_db())
from achievements import ACHIEVEMENT_DEFS
from database import Achievement
existing = db_seed.query(Achievement).count()
if existing == 0:
for defn in ACHIEVEMENT_DEFS:
db_seed.add(Achievement(**defn))
db_seed.commit()
print(f"[Seed] Добавлено {len(ACHIEVEMENT_DEFS)} достижений")
else:
existing_names = {a.name for a in db_seed.query(Achievement.name).all()}
added = 0
for defn in ACHIEVEMENT_DEFS:
if defn["name"] not in existing_names:
db_seed.add(Achievement(**defn))
added += 1
if added:
db_seed.commit()
print(f"[Seed] Добавлено {added} новых достижений")
db_seed.close()
except Exception as e:
print(f"[Seed] {e}")
import traceback
traceback.print_exc()
async def broadcast_online_count():
"""Рассылает всем количество игроков онлайн"""