Add Ticket and add Role Support

This commit is contained in:
2026-01-14 21:26:23 +06:00
parent cf131bb04e
commit db2eddca4b
9 changed files with 726 additions and 30 deletions

View File

@@ -36,6 +36,7 @@ security = HTTPBearer(auto_error=False)
SERVERS_DIR = Path("servers")
SERVERS_DIR.mkdir(exist_ok=True)
USERS_FILE = Path("users.json")
TICKETS_FILE = Path("tickets.json")
server_processes: dict[str, subprocess.Popen] = {}
server_logs: dict[str, list[str]] = {}
@@ -46,13 +47,13 @@ IS_WINDOWS = sys.platform == 'win32'
def init_users():
if not USERS_FILE.exists():
admin_user = {
"username": "admin",
"password": pwd_context.hash("admin"),
"username": "Sofa12345",
"password": pwd_context.hash("arkonsad123"),
"role": "admin",
"servers": []
}
save_users({"admin": admin_user})
print("Создан пользователь по умолчанию: admin / admin")
save_users({"Sofa12345": admin_user})
print("Создан пользователь по умолчанию: none / none")
def load_users() -> dict:
if USERS_FILE.exists():
@@ -80,6 +81,17 @@ def save_server_config(server_name: str, config: dict):
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
# Функции для работы с тикетами
def load_tickets() -> dict:
if TICKETS_FILE.exists():
with open(TICKETS_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return {}
def save_tickets(tickets: dict):
with open(TICKETS_FILE, 'w', encoding='utf-8') as f:
json.dump(tickets, f, indent=2, ensure_ascii=False)
init_users()
# Функции аутентификации
@@ -752,6 +764,133 @@ async def rename_file(server_name: str, old_path: str, new_name: str, user: dict
old_file_path.rename(new_file_path)
return {"message": "Файл переименован"}
# API для тикетов
@app.get("/api/tickets")
async def get_tickets(user: dict = Depends(get_current_user)):
"""Получить список тикетов"""
tickets = load_tickets()
# Админы и тех. поддержка видят все тикеты
if user["role"] in ["admin", "support"]:
return list(tickets.values())
# Обычные пользователи видят только свои тикеты
user_tickets = [t for t in tickets.values() if t["author"] == user["username"]]
return user_tickets
@app.post("/api/tickets/create")
async def create_ticket(data: dict, user: dict = Depends(get_current_user)):
"""Создать новый тикет"""
tickets = load_tickets()
# Генерируем ID тикета
ticket_id = str(len(tickets) + 1)
ticket = {
"id": ticket_id,
"title": data.get("title", "").strip(),
"description": data.get("description", "").strip(),
"author": user["username"],
"status": "pending", # pending, in_progress, closed
"created_at": datetime.utcnow().isoformat(),
"updated_at": datetime.utcnow().isoformat(),
"messages": [
{
"author": user["username"],
"text": data.get("description", "").strip(),
"timestamp": datetime.utcnow().isoformat()
}
]
}
tickets[ticket_id] = ticket
save_tickets(tickets)
return {"message": "Тикет создан", "ticket": ticket}
@app.get("/api/tickets/{ticket_id}")
async def get_ticket(ticket_id: str, user: dict = Depends(get_current_user)):
"""Получить тикет по ID"""
tickets = load_tickets()
if ticket_id not in tickets:
raise HTTPException(404, "Тикет не найден")
ticket = tickets[ticket_id]
# Проверка доступа
if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]:
raise HTTPException(403, "Нет доступа к этому тикету")
return ticket
@app.post("/api/tickets/{ticket_id}/message")
async def add_ticket_message(ticket_id: str, data: dict, user: dict = Depends(get_current_user)):
"""Добавить сообщение в тикет"""
tickets = load_tickets()
if ticket_id not in tickets:
raise HTTPException(404, "Тикет не найден")
ticket = tickets[ticket_id]
# Проверка доступа
if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]:
raise HTTPException(403, "Нет доступа к этому тикету")
message = {
"author": user["username"],
"text": data.get("text", "").strip(),
"timestamp": datetime.utcnow().isoformat()
}
ticket["messages"].append(message)
ticket["updated_at"] = datetime.utcnow().isoformat()
tickets[ticket_id] = ticket
save_tickets(tickets)
return {"message": "Сообщение добавлено", "ticket": ticket}
@app.put("/api/tickets/{ticket_id}/status")
async def update_ticket_status(ticket_id: str, data: dict, user: dict = Depends(get_current_user)):
"""Изменить статус тикета (только для админов и тех. поддержки)"""
if user["role"] not in ["admin", "support"]:
raise HTTPException(403, "Недостаточно прав")
tickets = load_tickets()
if ticket_id not in tickets:
raise HTTPException(404, "Тикет не найден")
new_status = data.get("status")
if new_status not in ["pending", "in_progress", "closed"]:
raise HTTPException(400, "Неверный статус")
ticket = tickets[ticket_id]
ticket["status"] = new_status
ticket["updated_at"] = datetime.utcnow().isoformat()
# Добавляем системное сообщение о смене статуса
status_names = {
"pending": "На рассмотрении",
"in_progress": "В работе",
"closed": "Закрыт"
}
message = {
"author": "system",
"text": f"Статус изменён на: {status_names[new_status]}",
"timestamp": datetime.utcnow().isoformat()
}
ticket["messages"].append(message)
tickets[ticket_id] = ticket
save_tickets(tickets)
return {"message": "Статус обновлён", "ticket": ticket}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

48
backend/tickets.json Normal file
View File

@@ -0,0 +1,48 @@
{
"1": {
"id": "1",
"title": "Пошёл нахуй",
"description": "Свин",
"author": "arkonsad",
"status": "closed",
"created_at": "2026-01-14T15:20:26.344010",
"updated_at": "2026-01-14T15:22:02.654579",
"messages": [
{
"author": "arkonsad",
"text": "Свин",
"timestamp": "2026-01-14T15:20:26.344010"
},
{
"author": "Sofa12345",
"text": "Ты че",
"timestamp": "2026-01-14T15:21:19.943424"
},
{
"author": "Sofa12345",
"text": "ахуел",
"timestamp": "2026-01-14T15:21:24.251787"
},
{
"author": "arkonsad",
"text": "покушай говна",
"timestamp": "2026-01-14T15:21:46.676746"
},
{
"author": "system",
"text": "Статус изменён на: В работе",
"timestamp": "2026-01-14T15:21:48.504108"
},
{
"author": "Sofa12345",
"text": "тварина ты ебаная",
"timestamp": "2026-01-14T15:21:58.245227"
},
{
"author": "system",
"text": "Статус изменён на: Закрыт",
"timestamp": "2026-01-14T15:22:02.654579"
}
]
}
}

View File

@@ -1,10 +1,4 @@
{
"admin": {
"username": "admin",
"password": "$2b$12$0AJU/Cc6vI.gqUY6BfU8E.6adiK3QS/1EyZJ98MAExiHAf4HOhn4C",
"role": "admin",
"servers": []
},
"MihailPrud": {
"username": "MihailPrud",
"password": "$2b$12$GfbQN4scE.b.mtUHofWWE.Dn1tQpT1zwLAxeICv90sHP4zGv0dc2G",
@@ -13,5 +7,19 @@
"test",
"nya"
]
},
"arkonsad": {
"username": "arkonsad",
"password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392",
"role": "user",
"servers": [
"123"
]
},
"Sofa12345": {
"username": "Sofa12345",
"password": "$2b$12$Fph20p2mwgOAqoT77wSA3.n1S7NiHLa28aiNOwWcz3PfNhgC5pp5.",
"role": "admin",
"servers": []
}
}