import os
import subprocess
import uuid
import yaml
import paramiko
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS
from flask_jwt_extended import (
    JWTManager, create_access_token,
    jwt_required, get_jwt_identity
)
from passlib.hash import pbkdf2_sha256
from logging.handlers import RotatingFileHandler
import json
from datetime import datetime, timedelta
import random
import logging
import string
import user_agents
import traceback
from werkzeug.exceptions import BadRequest
from jinja2 import Template

# Импортируйте SSH-конфигурацию
try:
    from config import SSH_CONFIG
except ImportError:
    SSH_CONFIG = {
        'hostname': '78.46.62.242',
        'port': 22,
        'username': 'root',
        'password': 'imp6Vjj8s4z3Ui',  # Используйте, если не используете ключи
    }

app = Flask(__name__, static_folder='static', template_folder='templates')
CORS(app)

# JWT Configuration
app.config['JWT_SECRET_KEY'] = 'gbauy45bguyab3e4gbaehrbghbg'  # Измените в продакшене
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
jwt = JWTManager(app)

OS_OPTIONS = {
    'win2020': 'Windows Server 2020',
    'win2022': 'Windows Server 2022',
    'win2025': 'Windows Server 2025',
    'win10': 'Windows 10',
    'win11': 'Windows 11'
}

IP_POOL_START = [192, 168, 1, 100]  # Starting IP address
current_ip = IP_POOL_START.copy()

CONTAINERS_DIR = os.path.join(os.getcwd(), 'containers')
TEMPLATES_DIR = os.path.join(CONTAINERS_DIR, 'templates')
os.makedirs(TEMPLATES_DIR, exist_ok=True)

# Сохраните шаблон Docker Compose, если он еще не существует
TEMPLATE_PATH = os.path.join(TEMPLATES_DIR, 'docker-compose-template.yml')
if not os.path.exists(TEMPLATE_PATH):
    with open(TEMPLATE_PATH, 'w') as f:
        f.write("""version: '3'
services:
  windows:
    image: dockurr/windows  # Убедитесь, что такой образ существует или замените на корректный
    container_name: {{ container_name }}
    environment:
      VERSION: "{{ version }}"
      DISK_SIZE: "{{ disk_size }}"
      RAM_SIZE: "{{ ram_size }}"
      CPU_CORES: "{{ cpu_cores }}"
      USERNAME: "{{ username }}"
      PASSWORD: "{{ password }}"
      KEYBOARD: "{{ keyboard }}"
    devices:
      - /dev/kvm
    cap_add:
      - NET_ADMIN
    ports:
      - "{{ public_port_8006 }}:8006/tcp"
      - "{{ public_port_rdp_tcp }}:3389/tcp"
      - "{{ public_port_rdp_udp }}:3389/udp"
    stop_grace_period: 2m
    restart: unless-stopped
""")

def generate_unique_ip():
    global current_ip
    ip = '.'.join(map(str, current_ip))
    current_ip[3] += 1
    if current_ip[3] > 254:
        current_ip[3] = 1
        current_ip[2] += 1
        if current_ip[2] > 254:
            current_ip[2] = 1
            current_ip[1] += 1
            if current_ip[1] > 254:
                current_ip[1] = 1
                current_ip[0] += 1
                if current_ip[0] > 254:
                    raise Exception("IP Pool Exhausted")
    return ip

@app.route('/')
def home():
    return render_template('index.html')

def setup_logging():
    # Create logs directory if it doesn't exist
    if not os.path.exists('logs'):
        os.makedirs('logs')
    
    # Configure main application logger
    formatter = logging.Formatter(
        '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
    )
    
    # File handler for general logs
    file_handler = RotatingFileHandler(
        'logs/app.log', 
        maxBytes=1024 * 1024,  # 1MB
        backupCount=10
    )
    file_handler.setFormatter(formatter)
    file_handler.setLevel(logging.INFO)
    
    # File handler for errors
    error_file_handler = RotatingFileHandler(
        'logs/error.log',
        maxBytes=1024 * 1024,  # 1MB
        backupCount=10
    )
    error_file_handler.setFormatter(formatter)
    error_file_handler.setLevel(logging.ERROR)
    
    # Configure Flask logger
    app.logger.addHandler(file_handler)
    app.logger.addHandler(error_file_handler)
    app.logger.setLevel(logging.INFO)
    
    # Also log basic request info
    @app.before_request
    def log_request_info():
        app.logger.info(f'Request: {request.method} {request.url} from {request.remote_addr}')
        app.logger.debug(f'Headers: {dict(request.headers)}')
        
        if request.method in ['POST', 'PUT', 'PATCH']:
            if request.is_json:
                try:
                    json_body = request.get_json()
                    app.logger.debug(f'JSON Body: {json_body}')
                except BadRequest as e:
                    app.logger.warning(f'Failed to parse JSON body: {e}')
            else:
                app.logger.debug('No JSON body present in the request.')
        else:
            app.logger.debug('Request method does not contain a JSON body.')
    
    # Add error handler
    @app.errorhandler(Exception)
    def handle_exception(e):
        app.logger.error(f'Unhandled Exception: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({
            'error': 'An internal server error occurred',
            'details': str(e) if app.debug else None
        }), 500

def load_db():
    try:
        if not os.path.exists('db.json'):
            app.logger.info('Creating new database file')
            with open('db.json', 'w') as f:
                json.dump({
                    'users': {},
                    'server_plans': [
                        {
                            'name': 'План 1',
                            'specs': '1 vCPU, 2 GB RAM',
                            'price': 10
                        },
                        {
                            'name': 'План 2',
                            'specs': '2 vCPU, 4 GB RAM',
                            'price': 20
                        }
                    ],
                    'logs': []  # Ensure logs array exists in initial DB structure
                }, f)
        with open('db.json', 'r') as f:
            db = json.load(f)
            # Ensure logs array exists even in existing DB files
            if 'logs' not in db:
                db['logs'] = []
                save_db(db)
            return db
    except Exception as e:
        app.logger.error(f'Database load error: {str(e)}')
        app.logger.error(traceback.format_exc())
        raise

def save_db(db):
    with open('db.json', 'w') as f:
        json.dump(db, f, indent=2)

def log_activity(username, action, user_agent_string):
    try:
        db = load_db()
        ua = user_agents.parse(user_agent_string)
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'username': username,
            'action': action,
            'browser': ua.browser.family,
            'os': ua.os.family,
            'device': ua.device.family,
            'ip': request.remote_addr
        }
        
        if 'logs' not in db:
            db['logs'] = []
            
        db['logs'].append(log_entry)
        save_db(db)
    except Exception as e:
        app.logger.error(f'Error logging activity: {str(e)}')
        app.logger.error(traceback.format_exc())

def generate_password():
    return ''.join(random.choices(string.ascii_letters + string.digits, k=12))

def execute_ssh_command(command):
    """
    Подключается к удалённому серверу по SSH и выполняет переданную команду.
    Возвращает stdout, stderr.
    """
    try:
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        # Подключение с использованием SSH-ключа
        ssh.connect(
            hostname=SSH_CONFIG['hostname'],
            port=SSH_CONFIG.get('port', 22),
            username=SSH_CONFIG['username'],
            key_filename=SSH_CONFIG['key_filepath'],
            # Если используете пароль, используйте параметр password
            # password=SSH_CONFIG.get('password')
        )
        
        stdin, stdout, stderr = ssh.exec_command(command)
        stdout_str = stdout.read().decode()
        stderr_str = stderr.read().decode()
        ssh.close()
        return stdout_str, stderr_str
    except Exception as e:
        app.logger.error(f'SSH command execution error: {e}')
        return None, str(e)

@app.route('/api/login', methods=['POST'])
def login():
    try:
        app.logger.info('Processing login request')
        db = load_db()
        data = request.get_json()
        
        if not data:
            app.logger.warning('No JSON data in login request')
            return jsonify({'error': 'No JSON data provided'}), 400
        
        username = data.get('username')
        password = data.get('password')
        
        app.logger.info(f'Login attempt for user: {username}')
        
        if not username or not password:
            app.logger.warning('Missing username or password in login request')
            return jsonify({'error': 'Username and password are required'}), 400
        
        user_data = db['users'].get(username)
        if not user_data:
            app.logger.warning(f'Login failed: User {username} not found')
            return jsonify({'error': 'Invalid credentials'}), 401
            
        if pbkdf2_sha256.verify(password, user_data['password']):
            app.logger.info(f'Login successful for user: {username}')
            access_token = create_access_token(identity=username)
            try:
                log_activity(username, 'login', request.headers.get('User-Agent', ''))
            except Exception as e:
                app.logger.error(f'Failed to log login activity: {str(e)}')
                # Continue with login even if logging fails
            return jsonify({
                'token': access_token,
                'username': username,
                'balance': user_data.get('balance', 0)
            })
        
        app.logger.warning(f'Login failed: Invalid password for user {username}')
        return jsonify({'error': 'Invalid credentials'}), 401
        
    except Exception as e:
        app.logger.error(f'Login error: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({'error': 'An internal server error occurred'}), 500

@app.route('/api/register', methods=['POST'])
def register():
    try:
        app.logger.info('Processing registration request')
        db = load_db()
        data = request.get_json()
        
        if not data:
            app.logger.warning('No JSON data in registration request')
            return jsonify({'error': 'No JSON data provided'}), 400
        
        username = data.get('username')
        password = data.get('password')
        
        app.logger.info(f'Registration attempt for username: {username}')
        
        if not username or not password:
            app.logger.warning('Missing username or password in registration request')
            return jsonify({'error': 'Username and password are required'}), 400
        
        if username in db['users']:
            app.logger.warning(f'Registration failed: Username {username} already exists')
            return jsonify({'error': 'Username already taken'}), 400
        
        hashed_password = pbkdf2_sha256.hash(password)
        
        db['users'][username] = {
            'password': hashed_password,
            'servers': [],
            'balance': 0,
            'tickets': [],
            'created_at': datetime.now().isoformat()
        }
        
        save_db(db)
        app.logger.info(f'Registration successful for user: {username}')
        log_activity(username, 'register', request.headers.get('User-Agent', ''))
        return jsonify({'message': 'Registration successful'}), 201
        
    except Exception as e:
        app.logger.error(f'Registration error: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({'error': 'An internal server error occurred'}), 500

@app.route('/api/servers', methods=['GET', 'POST'])
@jwt_required()
def servers():
    current_user = get_jwt_identity()
    db = load_db()
    
    if request.method == 'GET':
        user_data = db['users'].get(current_user)
        if not user_data:
            return jsonify({'error': 'User not found'}), 404
            
        return jsonify(user_data.get('servers', []))
    
    elif request.method == 'POST':
        data = request.get_json()
        if not data or 'plan' not in data or 'os' not in data or 'name' not in data:
            return jsonify({'error': 'Invalid request data'}), 400
            
        user_data = db['users'].get(current_user)
        
        if not user_data:
            return jsonify({'error': 'User not found'}), 404
            
        if user_data['balance'] < data['plan']['price']:
            return jsonify({'error': 'Insufficient funds'}), 400
            
        if data['os'] not in OS_OPTIONS:
            return jsonify({'error': 'Invalid operating system selected'}), 400
            
        # Генерация уникального IP адреса
        try:
            server_ip = generate_unique_ip()
        except Exception as e:
            app.logger.error(f'IP generation error: {str(e)}')
            return jsonify({'error': 'Unable to generate unique IP address'}), 500
        
        # Генерация уникального имени контейнера
        container_name = f"server_{uuid.uuid4().hex[:8]}"
        
        # Генерация пароля для RDP
        rdp_password = generate_password()
        
        # Порты
        public_port_8006 = random.randint(10000, 60000)
        public_port_rdp_tcp = random.randint(10000, 60000)
        public_port_rdp_udp = public_port_rdp_tcp  # Обычно используются одинаковые порты для TCP и UDP
        
        # План сервера
        plan = data['plan']
        
        # Операционная система
        os_option = data['os']
        
        # Имя сервера
        server_name = data['name']
        
        # Генерация пути для сервера
        server_dir = os.path.join(CONTAINERS_DIR, container_name)
        os.makedirs(server_dir, exist_ok=True)
        
        # Загрузка шаблона
        with open(TEMPLATE_PATH, 'r') as f:
            template = Template(f.read())
        
        # Рендеринг шаблона
        docker_compose_content = template.render(
            container_name=container_name,
            version=os_option,  # предполагается, что ключ VERSION соответствует выбранной ОС
            disk_size="100G",    # можно сделать динамическим
            ram_size="8G",       # можно сделать динамическим
            cpu_cores="4",       # можно сделать динамическим
            username=data.get('rdp_username', 'admin'),  # можно добавить в запрос
            password=rdp_password,
            keyboard="ru-RU",
            public_port_8006=public_port_8006,
            public_port_rdp_tcp=public_port_rdp_tcp,
            public_port_rdp_udp=public_port_rdp_udp
        )
        
        # Запись docker-compose.yml
        docker_compose_path = os.path.join(server_dir, 'docker-compose.yml')
        with open(docker_compose_path, 'w') as f:
            f.write(docker_compose_content)
        
        # Команды для создания и запуска контейнера на удалённом сервере
        # Можно использовать SSH для копирования файлов и выполнения команд
        # Например:
        # 1. Копировать директорию контейнера на удалённый сервер
        # 2. Выполнить `docker-compose up -d` в директории контейнера
        
        # Сжатие директории для передачи
        zip_filename = f"{container_name}.tar.gz"
        try:
            subprocess.run(['tar', '-czf', zip_filename, '-C', CONTAINERS_DIR, container_name], check=True)
        except subprocess.CalledProcessError as e:
            app.logger.error(f'Error creating archive: {e}')
            return jsonify({'error': 'Failed to prepare server files'}), 500
        
        # Передача архива на удалённый сервер с помощью SCP
        try:
            scp_cmd = [
                'scp',
                '-P', str(SSH_CONFIG.get('port', 22)),
                zip_filename,
                f"{SSH_CONFIG['username']}@{SSH_CONFIG['hostname']}:/tmp/{zip_filename}"
            ]
            subprocess.run(scp_cmd, check=True)
        except subprocess.CalledProcessError as e:
            app.logger.error(f'SCP transfer error: {e}')
            return jsonify({'error': 'Failed to transfer server files to remote host'}), 500
        
        # Разархивирование и запуск контейнера на удалённом сервере
        commands = [
            f"tar -xzf /tmp/{zip_filename} -C {CONTAINERS_DIR}",
            f"rm /tmp/{zip_filename}",
            f"cd {os.path.join(CONTAINERS_DIR, container_name)} && docker-compose up -d"
        ]
        full_command = ' && '.join(commands)
        
        stdout, stderr = execute_ssh_command(full_command)
        if stderr:
            app.logger.error(f'Error executing SSH commands: {stderr}')
            return jsonify({'error': f'Failed to execute commands on remote host: {stderr}'}), 500
        
        # Удаление локального архива
        os.remove(zip_filename)
        
        # Добавление сервера в базу данных
        new_server = {
            'id': len(user_data['servers']) + 1,
            'name': server_name,
            'plan': plan,
            'os': OS_OPTIONS[os_option],
            'status': 'active',
            'password': rdp_password,
            'ip': server_ip,
            'created_at': datetime.now().isoformat(),
            'auto_renewal': True,
            'container_name': container_name,
            'ports': {
                '8006/tcp': public_port_8006,
                '3389/tcp': public_port_rdp_tcp,
                '3389/udp': public_port_rdp_udp
            }
        }
        
        user_data['balance'] -= plan['price']
        user_data['servers'].append(new_server)
        save_db(db)
        log_activity(current_user, 'create_server', request.headers.get('User-Agent', ''))
        return jsonify(new_server), 201

@app.route('/api/servers/<int:server_id>', methods=['GET'])
@jwt_required()
def get_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    return jsonify(server), 200

@app.route('/api/servers/<int:server_id>/toggle', methods=['POST'])
@jwt_required()
def toggle_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    container_name = server.get('container_name')
    if not container_name:
        return jsonify({'error': 'Container name not found for this server'}), 400

    # Определение команды для остановки или запуска контейнера
    if server['status'] == 'active':
        cmd = f"cd {os.path.join(CONTAINERS_DIR, container_name)} && docker-compose stop"
    else:
        cmd = f"cd {os.path.join(CONTAINERS_DIR, container_name)} && docker-compose start"
    
    stdout, stderr = execute_ssh_command(cmd)
    if stderr:
        app.logger.error(f'SSH command error: {stderr}')
        return jsonify({'error': f'Failed to toggle server status: {stderr}'}), 500
    
    # Обновление статуса
    server['status'] = 'frozen' if server['status'] == 'active' else 'active'
    save_db(db)
    log_activity(current_user, f'toggle_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': f"Server status changed to {server['status']}"}), 200

@app.route('/api/servers/<int:server_id>', methods=['DELETE'])
@jwt_required()
def delete_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    container_name = server.get('container_name')
    if not container_name:
        return jsonify({'error': 'Container name not found for this server'}), 400

    # Команды для остановки и удаления контейнера на удалённом сервере
    commands = [
        f"cd {os.path.join(CONTAINERS_DIR, container_name)} && docker-compose down",
        f"rm -rf {os.path.join(CONTAINERS_DIR, container_name)}"
    ]
    full_command = ' && '.join(commands)
    
    stdout, stderr = execute_ssh_command(full_command)
    if stderr:
        app.logger.error(f'SSH command error: {stderr}')
        return jsonify({'error': f'Failed to delete server: {stderr}'}), 500

    # Удаление локальных архива (если есть)
    zip_filename = f"{container_name}.tar.gz"
    if os.path.exists(zip_filename):
        os.remove(zip_filename)
    
    # Удаление сервера из базы данных
    user_data['servers'].remove(server)
    save_db(db)
    log_activity(current_user, f'delete_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': 'Server deleted successfully'}), 200

@app.route('/api/servers/<int:server_id>', methods=['PUT'])
@jwt_required()
def update_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    data = request.get_json()
    if not data:
        return jsonify({'error': 'No data provided'}), 400

    # Example: Allow updating server name and OS
    new_name = data.get('name')
    new_os = data.get('os')

    if new_name:
        server['name'] = new_name

    if new_os:
        if new_os not in OS_OPTIONS:
            return jsonify({'error': 'Invalid operating system selected'}), 400
        # Обновление Docker Compose файла
        container_name = server.get('container_name')
        server_dir = os.path.join(CONTAINERS_DIR, container_name)
        docker_compose_path = os.path.join(server_dir, 'docker-compose.yml')
        
        if not os.path.exists(docker_compose_path):
            return jsonify({'error': 'docker-compose.yml not found for this server'}), 400
        
        # Загрузка текущего docker-compose.yml
        try:
            with open(docker_compose_path, 'r') as f:
                compose_data = yaml.safe_load(f)
        except Exception as e:
            app.logger.error(f'Error reading docker-compose.yml: {e}')
            return jsonify({'error': 'Failed to read docker-compose.yml'}), 500
        
        # Обновление версии ОС
        compose_data['services']['windows']['environment']['VERSION'] = new_os
        compose_data['services']['windows']['os'] = OS_OPTIONS[new_os]
        
        # Запись обновленного docker-compose.yml
        try:
            with open(docker_compose_path, 'w') as f:
                yaml.dump(compose_data, f)
        except Exception as e:
            app.logger.error(f'Error writing docker-compose.yml: {e}')
            return jsonify({'error': 'Failed to update docker-compose.yml'}), 500
        
        # Перезапуск контейнера на удалённом сервере для применения изменений
        cmd = f"cd {server_dir} && docker-compose up -d"
        stdout, stderr = execute_ssh_command(cmd)
        if stderr:
            app.logger.error(f'SSH command error: {stderr}')
            return jsonify({'error': f'Failed to restart server: {stderr}'}), 500

        # Обновление поля ОС в базе данных
        server['os'] = OS_OPTIONS[new_os]

    save_db(db)
    log_activity(current_user, f'update_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': 'Server updated successfully', 'server': server}), 200

@app.route('/api/userinfo', methods=['GET', 'PUT'])
@jwt_required()
def userinfo():
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    if request.method == 'GET':
        return jsonify({'username': current_user, 'balance': user_data.get('balance', 0)}), 200
    
    elif request.method == 'PUT':
        data = request.get_json()
        if not data:
            return jsonify({'error': 'No data provided'}), 400
        
        current_password = data.get('current_password')
        new_username = data.get('new_username')
        new_password = data.get('new_password')

        if not current_password:
            return jsonify({'error': 'Current password is required'}), 400

        # Verify current password
        if not pbkdf2_sha256.verify(current_password, user_data['password']):
            return jsonify({'error': 'Current password is incorrect'}), 401

        # Update username if provided
        if new_username:
            if new_username in db['users']:
                return jsonify({'error': 'Username already taken'}), 400
            db['users'][new_username] = db['users'].pop(current_user)
            current_user = new_username

        # Update password if provided
        if new_password:
            db['users'][current_user]['password'] = pbkdf2_sha256.hash(new_password)

        save_db(db)
        log_activity(current_user, 'update_userinfo', request.headers.get('User-Agent', ''))
        return jsonify({'message': 'User information updated successfully', 'username': current_user}), 200

@app.route('/api/tickets', methods=['GET', 'POST'])
@jwt_required()
def tickets():
    current_user = get_jwt_identity()
    db = load_db()
    
    if request.method == 'GET':
        user_data = db['users'].get(current_user)
        if not user_data:
            return jsonify({'error': 'User not found'}), 404
            
        return jsonify(user_data.get('tickets', []))
    
    elif request.method == 'POST':
        data = request.get_json()
        if not data or 'title' not in data or 'description' not in data:
            return jsonify({'error': 'Invalid request data'}), 400
            
        user_data = db['users'].get(current_user)
        if not user_data:
            return jsonify({'error': 'User not found'}), 404
            
        new_ticket = {
            'id': len(user_data['tickets']) + 1,
            'title': data['title'],
            'description': data.get('description', ''),
            'status': 'open',
            'created_at': datetime.now().isoformat(),
            'messages': []
        }
        
        user_data['tickets'].append(new_ticket)
        save_db(db)
        log_activity(current_user, 'create_ticket', request.headers.get('User-Agent', ''))
        return jsonify(new_ticket), 201

@app.route('/api/tickets/<int:ticket_id>', methods=['GET'])
@jwt_required()
def get_ticket(ticket_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    ticket = next((t for t in user_data['tickets'] if t['id'] == ticket_id), None)
    if not ticket:
        return jsonify({'error': 'Ticket not found'}), 404

    return jsonify(ticket), 200

@app.route('/api/tickets/<int:ticket_id>/reply', methods=['POST'])
@jwt_required()
def reply_ticket(ticket_id):
    current_user = get_jwt_identity()
    db = load_db()
    
    user_data = db['users'].get(current_user)
    if not user_data:
        return jsonify({'error': 'User not found'}), 404
        
    ticket = next((t for t in user_data['tickets'] if t['id'] == ticket_id), None)
    if not ticket:
        return jsonify({'error': 'Ticket not found'}), 404
        
    data = request.get_json()
    if not data or 'message' not in data:
        return jsonify({'error': 'Message is required'}), 400
        
    message = {
        'content': data['message'],
        'sender': current_user,
        'timestamp': datetime.now().isoformat()
    }
    
    ticket['messages'].append(message)
    save_db(db)
    log_activity(current_user, f'reply_ticket_{ticket_id}', request.headers.get('User-Agent', ''))
    return jsonify(message), 201

if __name__ == '__main__':
    setup_logging()
    app.run(debug=True)



[2024-11-01 22:03:02,297] INFO in main: Request: POST http://niggers.drugdeal.store/api/servers from 127.0.0.1
[2024-11-01 22:03:02,298] INFO in main: Request: POST http://niggers.drugdeal.store/api/servers from 127.0.0.1
root@78.46.62.242's password: root@78.46.62.242's password: 
Permission denied, please try again.
root@78.46.62.242's password: 
Permission denied, please try again.
root@78.46.62.242's password: 
server_1c097491.tar.gz                                                                                    100%  508     2.9KB/s   00:00    
[2024-11-01 22:03:29,911] ERROR in main: SSH command execution error: 'key_filepath'
[2024-11-01 22:03:29,913] ERROR in main: Error executing SSH commands: 'key_filepath'



authentification don't needs to be performed manually only automated auth