feat: auto delete server if it is inactive for more than 5 minutes, minor fix

This commit is contained in:
2025-07-18 20:34:11 +05:00
parent 7e4e2c0bad
commit 259e3c373b
7 changed files with 126 additions and 5 deletions

View File

@ -2,6 +2,8 @@ from fastapi import APIRouter
from app.services.server.command import CommandService from app.services.server.command import CommandService
from app.services.server.event import EventService from app.services.server.event import EventService
from app.models.server.command import ServerCommand from app.models.server.command import ServerCommand
from datetime import datetime
import uuid
router = APIRouter( router = APIRouter(
prefix="/api/server", prefix="/api/server",
@ -10,12 +12,29 @@ router = APIRouter(
@router.post("/events") @router.post("/events")
async def receive_server_event(event_data: dict): async def receive_server_event(event_data: dict):
# Обновляем активность сервера
server_ip = event_data.get("server_ip")
if server_ip:
await update_server_activity(server_ip)
return await EventService().process_event(event_data) return await EventService().process_event(event_data)
@router.post("/commands") @router.post("/commands")
async def add_server_command(command_data: ServerCommand): async def add_server_command(command_data: ServerCommand):
# Обновляем last_activity для сервера
await CommandService()._update_server_activity(command_data.server_ip)
return await CommandService().add_command(command_data) return await CommandService().add_command(command_data)
@router.get("/commands") @router.get("/commands")
async def get_server_commands(server_ip: str): async def get_server_commands(server_ip: str):
return await CommandService().get_commands(server_ip) return await CommandService().get_commands(server_ip)
async def update_server_activity(server_ip):
"""Обновляет время последней активности сервера"""
from app.db.database import db
game_servers_collection = db.game_servers
await game_servers_collection.update_one(
{"ip": server_ip},
{"$set": {"last_activity": datetime.utcnow()}},
upsert=False
)

View File

@ -54,3 +54,8 @@ async def get_user_purchased_capes(username: str):
async def activate_purchased_cape(username: str, cape_id: str): async def activate_purchased_cape(username: str, cape_id: str):
"""Активация приобретенного плаща""" """Активация приобретенного плаща"""
return await store_cape_service.activate_purchased_cape(username, cape_id) return await store_cape_service.activate_purchased_cape(username, cape_id)
@router.post("/user/{username}/capes/deactivate/{cape_id}")
async def deactivate_purchased_cape(username: str, cape_id: str):
"""Деактивация приобретенного плаща"""
return await store_cape_service.deactivate_purchased_cape(username, cape_id)

View File

@ -20,6 +20,10 @@ class CommandService:
} }
print(f"[{datetime.now()}] Добавлена команда: {command_data.command} " print(f"[{datetime.now()}] Добавлена команда: {command_data.command} "
f"для сервера {command_data.server_ip}") f"для сервера {command_data.server_ip}")
# Обновляем last_activity для сервера
await self._update_server_activity(command_data.server_ip)
return {"status": "success", "command_id": command_id} return {"status": "success", "command_id": command_id}
except Exception as e: except Exception as e:
raise HTTPException(status_code=400, detail=str(e)) raise HTTPException(status_code=400, detail=str(e))
@ -47,3 +51,14 @@ class CommandService:
return {"status": "success", "commands": commands} return {"status": "success", "commands": commands}
except Exception as e: except Exception as e:
raise HTTPException(status_code=400, detail=str(e)) raise HTTPException(status_code=400, detail=str(e))
async def _update_server_activity(self, server_ip):
"""Обновляет время последней активности для сервера"""
from app.db.database import db
game_servers_collection = db.game_servers
await game_servers_collection.update_one(
{"ip": server_ip},
{"$set": {"last_activity": datetime.utcnow()}},
upsert=False # Не создаем новый сервер, только обновляем существующий
)

View File

@ -101,11 +101,18 @@ class EventService:
"port": 25565, # Стандартный порт Minecraft "port": 25565, # Стандартный порт Minecraft
"description": f"Minecraft server {server_ip}", "description": f"Minecraft server {server_ip}",
"max_players": 100, "max_players": 100,
"registered_at": datetime.utcnow() "registered_at": datetime.utcnow(),
"last_activity": datetime.utcnow() # Добавляем поле last_activity
} }
await game_servers_collection.insert_one(server_data) await game_servers_collection.insert_one(server_data)
print(f"[{datetime.utcnow()}] Зарегистрирован новый сервер: {server_ip}") print(f"[{datetime.utcnow()}] Зарегистрирован новый сервер: {server_ip}")
else:
# Обновляем активность существующего сервера
await game_servers_collection.update_one(
{"ip": server_ip},
{"$set": {"last_activity": datetime.utcnow()}}
)
return existing_server or await game_servers_collection.find_one({"ip": server_ip}) return existing_server or await game_servers_collection.find_one({"ip": server_ip})
@ -114,11 +121,18 @@ class EventService:
from app.db.database import db from app.db.database import db
online_players_collection = db.online_players online_players_collection = db.online_players
game_servers_collection = db.game_servers
# Получаем ID сервера # Получаем ID сервера
server = await self._register_server(server_ip, {}) server = await self._register_server(server_ip, {})
server_id = server["id"] server_id = server["id"]
# Обновляем время активности сервера
await game_servers_collection.update_one(
{"id": server_id},
{"$set": {"last_activity": datetime.utcnow()}}
)
# Помечаем всех игроков как оффлайн на этом сервере # Помечаем всех игроков как оффлайн на этом сервере
await online_players_collection.update_many( await online_players_collection.update_many(
{"server_id": server_id}, {"server_id": server_id},

View File

@ -1,7 +1,7 @@
from fastapi import HTTPException from fastapi import HTTPException
from app.db.database import db, users_collection from app.db.database import db, users_collection
from app.models.server.prank import PrankCommand, PrankCommandUpdate from app.models.server.prank import PrankCommand, PrankCommandUpdate
from datetime import datetime from datetime import datetime, timedelta
import uuid import uuid
from app.services.server.command import CommandService from app.services.server.command import CommandService
@ -124,6 +124,29 @@ class PrankService:
async def get_all_servers(self): async def get_all_servers(self):
"""Получение списка всех доступных серверов""" """Получение списка всех доступных серверов"""
# Проверяем и удаляем неактивные серверы (более 5 минут без данных)
current_time = datetime.utcnow()
inactive_threshold = 5 * 60 # 5 минут в секундах
# Находим серверы, которые не отправляли данные больше 5 минут
# Учитываем, что у некоторых серверов может не быть поля last_activity
inactive_servers = await game_servers_collection.find({
"last_activity": {
"$exists": True,
"$lt": current_time - timedelta(seconds=inactive_threshold)
}
}).to_list(100)
# Удаляем неактивные серверы
if inactive_servers:
server_ids = [server["id"] for server in inactive_servers]
await game_servers_collection.delete_many({"id": {"$in": server_ids}})
# Опционально: логирование удаленных серверов
for server in inactive_servers:
print(f"Удален неактивный сервер: {server['name']} (ID: {server['id']})")
# Получаем актуальный список серверов
servers = await game_servers_collection.find().to_list(100) servers = await game_servers_collection.find().to_list(100)
# Если нет зарегистрированных серверов, вернем пустой список # Если нет зарегистрированных серверов, вернем пустой список
@ -144,7 +167,8 @@ class PrankService:
"port": server.get("port"), "port": server.get("port"),
"description": server.get("description", ""), "description": server.get("description", ""),
"online_players": online_count, "online_players": online_count,
"max_players": server.get("max_players", 0) "max_players": server.get("max_players", 0),
"last_activity": server.get("last_activity")
}) })
return result return result

View File

@ -131,11 +131,24 @@ class StoreCapeService:
except Exception as e: except Exception as e:
print(f"Ошибка при удалении файла: {e}") print(f"Ошибка при удалении файла: {e}")
# Удаляем из БД # Удаляем из БД плащей магазина
result = await store_capes_collection.delete_one({"id": cape_id}) result = await store_capes_collection.delete_one({"id": cape_id})
if result.deleted_count == 0: if result.deleted_count == 0:
raise HTTPException(status_code=500, detail="Ошибка при удалении из БД") raise HTTPException(status_code=500, detail="Ошибка при удалении из БД")
# Удаляем из БД купленных плащей
purchases_collection = db.purchases
purchases = await purchases_collection.find_one({"cape_id": cape_id})
if purchases:
await purchases_collection.delete_one({"cape_id": cape_id})
# Удаляем плащ из массива purchased_capes всех пользователей
users_collection = db.users
await users_collection.update_many(
{"purchased_capes.cape_id": cape_id},
{"$pull": {"purchased_capes": {"cape_id": cape_id}}}
)
return {"status": "success"} return {"status": "success"}
async def purchase_cape(self, username: str, cape_id: str): async def purchase_cape(self, username: str, cape_id: str):
@ -189,6 +202,7 @@ class StoreCapeService:
"purchased_capes": { "purchased_capes": {
"cape_id": cape_id, "cape_id": cape_id,
"cape_name": cape["name"], "cape_name": cape["name"],
"cape_description": cape["description"],
"file_name": cape_filename, "file_name": cape_filename,
"purchased_at": datetime.utcnow() "purchased_at": datetime.utcnow()
} }
@ -233,6 +247,7 @@ class StoreCapeService:
result.append({ result.append({
"cape_id": cape.get("cape_id"), "cape_id": cape.get("cape_id"),
"cape_name": cape.get("cape_name"), "cape_name": cape.get("cape_name"),
"cape_description": cape.get("cape_description"),
"image_url": f"{FILES_URL}/capes/{cape.get('file_name')}", "image_url": f"{FILES_URL}/capes/{cape.get('file_name')}",
"purchased_at": cape.get("purchased_at"), "purchased_at": cape.get("purchased_at"),
"is_active": user.get("cloak_url") == f"{FILES_URL}/capes/{cape.get('file_name')}" "is_active": user.get("cloak_url") == f"{FILES_URL}/capes/{cape.get('file_name')}"
@ -268,3 +283,32 @@ class StoreCapeService:
"status": "success", "status": "success",
"message": f"Плащ '{selected_cape.get('cape_name')}' активирован" "message": f"Плащ '{selected_cape.get('cape_name')}' активирован"
} }
async def deactivate_purchased_cape(self, username: str, cape_id: str):
"""Деактивация приобретенного плаща"""
user = await users_collection.find_one({"username": username})
if not user:
raise HTTPException(status_code=404, detail="Пользователь не найден")
# Проверяем, что плащ был приобретен
purchased_capes = user.get("purchased_capes", [])
selected_cape = None
for cape in purchased_capes:
if cape.get("cape_id") == cape_id:
selected_cape = cape
break
if not selected_cape:
raise HTTPException(status_code=404, detail="Плащ не найден среди приобретенных")
# Устанавливаем выбранный плащ
await users_collection.update_one(
{"username": username},
{"$set": {"cloak_url": None}}
)
return {
"status": "success",
"message": f"Плащ '{selected_cape.get('cape_name')}' деактивирован"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB