Вот код сервера:
```py
import json
import os
import shutil
from functools import wraps

from flask import Flask, request, jsonify, render_template, make_response, send_file
import jwt
import datetime

from DatabaseManager import DatabaseManager
from utils import convert_to_yolo_format, write_classes_txt

app = Flask(__name__)

# Секретный ключ для JWT
SECRET_KEY = 'your_secret_key'  # Замените на ваш секретный ключ
app.config['SECRET_KEY'] = SECRET_KEY

db_manager = DatabaseManager()

# Декоратор для защиты маршрутов, требующих аутентификации
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.cookies.get('auth_token')

        if not token:
            return jsonify({'message': 'Токен отсутствует! Пожалуйста, войдите в систему.'}), 401

        try:
            data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            current_user = db_manager.get_user_by_username(data['username'])
            if not current_user:
                raise Exception('Пользователь не найден')
        except jwt.ExpiredSignatureError:
            return jsonify({'message': 'Срок действия токена истек. Пожалуйста, войдите в систему снова.'}), 401
        except Exception as e:
            return jsonify({'message': 'Токен недействителен!'}), 401

        return f(current_user, *args, **kwargs)
    return decorated

#########################################################################################################
#########################################################################################################
#########################################################################################################

### Эндпоинты API

# Регистрация пользователя
@app.route('/api/register', methods=['POST'])
def register():
    data = request.json
    username = data.get('username')
    password = data.get('password')
    if username and password:
        success = db_manager.create_account(username, password)
        if success:
            return jsonify({'message': 'Аккаунт успешно создан'}), 201
        else:
            return jsonify({'message': 'Пользователь с таким именем уже существует'}), 400
    else:
        return jsonify({'message': 'Требуются логин и пароль'}), 400


# Аутентификация пользователя
@app.route('/api/login', methods=['POST'])
def login_api():
    data = request.json
    username = data.get('username')
    password = data.get('password')
    if username and password:
        valid = db_manager.verify_account(username, password)
        if valid:
            # Генерация JWT токена
            token = jwt.encode({
                'username': username,
                'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24)
            }, app.config['SECRET_KEY'], algorithm='HS256')

            # Создаем ответ и устанавливаем куки с токеном
            response = make_response(jsonify({'message': 'Вход выполнен успешно', 'token': token}))
            response.set_cookie('auth_token', token, httponly=True,
                                samesite='Strict')  # Можно добавить 'secure=True' при использовании HTTPS
            return response, 200
        else:
            return jsonify({'message': 'Неверные учетные данные'}), 401
    else:
        return jsonify({'message': 'Требуются логин и пароль'}), 400


# Выход из аккаунта
@app.route('/api/logout', methods=['POST'])
def logout_api():
    response = make_response(jsonify({'message': 'Выход выполнен успешно'}))
    response.set_cookie('auth_token', '', expires=0)
    return response, 200


# Эндпоинт для получения информации об аккаунте
@app.route('/api/account', methods=['GET'])
@token_required
def get_account_info(current_user):
    print("get_account_info")
    return jsonify({
        'id': current_user['id'],
        'username': current_user['username']
        # Добавьте другие необходимые поля
    }), 200


# Создание нового проекта
@app.route('/api/projects', methods=['POST'])
@token_required
def create_project(current_user):
    # Получаем данные из формы
    title = request.form.get('title')
    path = request.form.get('path')
    classes_json = request.form.get('classes')
    images = request.files.getlist('images')

    if not title:
        return jsonify({'message': 'Title is required'}), 400

    if not images and not path:
        return jsonify({'message': 'Please upload images or specify a path'}), 400

    # Парсируем классы
    try:
        classes = json.loads(classes_json)
    except:
        return jsonify({'message': 'Invalid classes data'}), 400

    # Обрабатываем изображения
    image_paths = []

    # Директория для сохранения изображений проекта
    project_dir = os.path.join('projects', str(current_user['id']), title)
    os.makedirs(project_dir, exist_ok=True)

    # Сохраняем загруженные изображения
    for image in images:
        filename = image.filename
        image_path = os.path.join(project_dir, filename)
        image.save(image_path)
        image_paths.append(image_path)

    # Если указан путь, используем изображения из него
    if path:
        if os.path.exists(path) and os.path.isdir(path):
            for root, dirs, files in os.walk(path):
                for file in files:
                    if file.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')):
                        file_path = os.path.join(root, file)
                        dest_path = os.path.join(project_dir, file)
                        shutil.copy2(file_path, dest_path)
                        image_paths.append(dest_path)
        else:
            return jsonify({'message': 'Invalid path specified'}), 400

    # Подготавливаем данные проекта
    project_data = {
        'title': title,
        'classes': classes,
        # Другие необходимые данные
    }

    # Создаем проект в базе данных
    author_id = current_user['id']
    user_ids = []  # Пока что только автор
    project_id = db_manager.create_project(author_id, user_ids, project_data, image_paths)

    # Write classes.txt
    write_classes_txt(project_dir, classes)

    return jsonify({'message': 'Проект создан', 'project_id': project_id}), 201


# Получение изображения для аннотации
@app.route('/api/get_image', methods=['GET'])
@token_required
def get_image(current_user):
    project_id = request.args.get('project_id', type=int)
    if not project_id:
        return jsonify({'message': 'Project ID is required'}), 400

    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    # Проверяем доступ пользователя к проекту
    if project['author_id'] != current_user['id'] and current_user['id'] not in project['user_ids']:
        return jsonify({'message': 'Access denied'}), 403

    # Получаем следующее неразмеченное изображение
    image_info = db_manager.get_next_image_for_annotation(project_id, current_user['id'])

    if not image_info:
        return jsonify({'message': 'No more images to annotate'}), 200

    # Получаем прогресс проекта
    total_images, annotated_images = db_manager.get_project_progress(project_id)
    progress = (annotated_images / total_images) * 100 if total_images > 0 else 0

    image_url = '/api/project_image/{}'.format(image_info['id'])

    response_data = {
        'image_id': image_info['id'],
        'image_url': image_url,
        'classes': project['data']['classes'],
        'current': annotated_images + 1,  # Предполагая, что мы собираемся аннотировать это изображение
        'total': total_images,
        'progress': progress,
        'project_title': project['data']['title']
    }
    return jsonify(response_data), 200


# Отправка аннотации для изображения
@app.route('/api/submit_annotation', methods=['POST'])
@token_required
def submit_annotation(current_user):
    data = request.json
    project_id = data.get('project_id')
    image_id = data.get('image_id')
    annotation = data.get('annotation')

    if not project_id or not image_id or annotation is None:
        return jsonify({'message': 'project_id, image_id and annotation are required'}), 400

    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    # Проверяем доступ пользователя к проекту
    if project['author_id'] != current_user['id'] and current_user['id'] not in project['user_ids']:
        return jsonify({'message': 'Access denied'}), 403

    # Сохраняем аннотацию в формате YOLO
    image_info = db_manager.get_project_image(image_id)
    if not image_info:
        return jsonify({'message': 'Image not found'}), 404

    # Проверяем, что изображение назначено текущему пользователю
    if image_info['assigned_to'] != current_user['id']:
        return jsonify({'message': 'This image is not assigned to you'}), 403

    # Путь к изображению
    image_path = image_info['image_path']
    annotation_path = os.path.splitext(image_path)[0] + '.txt'

    # Преобразуем аннотацию в формат YOLO
    yolo_annotation = convert_to_yolo_format(annotation)

    with open(annotation_path, 'w') as f:
        f.write(yolo_annotation)

    # Обновляем статус изображения на "annotated"
    db_manager.update_image_status(image_id, 'annotated')

    return jsonify({'message': 'Annotation submitted'}), 200


# Получение списка проектов пользователя
@app.route('/api/projects', methods=['GET'])
@token_required
def get_user_projects(current_user):
    projects = db_manager.get_projects_by_user(current_user['id'])
    return jsonify({'projects': projects}), 200


# Получение информации о проекте
@app.route('/api/projects/<int:project_id>', methods=['GET'])
@token_required
def get_project(current_user, project_id):
    project = db_manager.get_project(project_id)
    if project:
        # Check if user has access
        if project['author_id'] != current_user['id'] and current_user['id'] not in project['user_ids']:
            return jsonify({'message': 'Access denied'}), 403

        # Get total_images
        total_images = db_manager.get_project_images_count(project_id)
        project['total_images'] = total_images

        return jsonify(project), 200
    else:
        return jsonify({'message': 'Project not found'}), 404


# Обновление проекта
@app.route('/api/projects/<int:project_id>', methods=['PUT'])
@token_required
def update_project(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Проект не найден'}), 404

    # Проверка, является ли текущий пользователь автором проекта
    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Только автор проекта может его обновлять'}), 403

    data = request.json
    user_ids = data.get('user_ids')
    project_data = data.get('data')
    db_manager.update_project(project_id, user_ids=user_ids, data=project_data)
    return jsonify({'message': 'Проект обновлен'}), 200


# Сервис для безопасной выдачи изображений
@app.route('/api/project_image/<int:image_id>')
@token_required
def project_image(current_user, image_id):
    image_info = db_manager.get_project_image(image_id)
    if not image_info:
        return "Image not found", 404

    project = db_manager.get_project(image_info['project_id'])
    if not project:
        return "Project not found", 404

    # Проверяем доступ пользователя к проекту
    if project['author_id'] != current_user['id'] and current_user['id'] not in project['user_ids']:
        return "Access denied", 403

    return send_file(image_info['image_path'])


# Update project title
@app.route('/api/projects/<int:project_id>/update_title', methods=['POST'])
@token_required
def update_project_title(current_user, project_id):
    data = request.json
    new_title = data.get('title')

    if not new_title:
        return jsonify({'message': 'Title is required'}), 400

    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Only the author can update the project title'}), 403

    # Update the project's data
    project_data = project['data']
    project_data['title'] = new_title
    db_manager.update_project(project_id, data=project_data)

    return jsonify({'message': 'Project title updated successfully'}), 200


# Get images of a project with pagination
@app.route('/api/projects/<int:project_id>/images', methods=['GET'])
@token_required
def get_project_images(current_user, project_id):
    page = request.args.get('page', default=1, type=int)
    per_page = request.args.get('per_page', default=20, type=int)

    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Access denied'}), 403

    images = db_manager.get_project_images_paginated(project_id, page, per_page)
    total_images = db_manager.get_project_images_count(project_id)

    return jsonify({'images': images, 'total_images': total_images}), 200


# Delete an image from a project
@app.route('/api/projects/<int:project_id>/images/<int:image_id>', methods=['DELETE'])
@token_required
def delete_project_image(current_user, project_id, image_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Access denied'}), 403

    image_info = db_manager.get_project_image(image_id)
    if not image_info or image_info['project_id'] != project_id:
        return jsonify({'message': 'Image not found in this project'}), 404

    # Delete image file
    try:
        os.remove(image_info['image_path'])
        annotation_path = os.path.splitext(image_info['image_path'])[0] + '.txt'
        if os.path.exists(annotation_path):
            os.remove(annotation_path)
    except Exception as e:
        print(f"Error deleting image file: {e}")

    # Delete image from database
    db_manager.delete_project_image(image_id)

    return jsonify({'message': 'Image deleted successfully'}), 200


# Delete a project
@app.route('/api/projects/<int:project_id>', methods=['DELETE'])
@token_required
def delete_project(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Only the author can delete the project'}), 403

    db_manager.delete_project(project_id)

    return jsonify({'message': 'Project deleted successfully'}), 200


# Get project info for joining
@app.route('/api/projects/<int:project_id>/info', methods=['GET'])
@token_required
def get_project_info(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    project_info = {
        'id': project['id'],
        'title': project['data'].get('title', 'Untitled'),
        'author': db_manager.get_user_by_id(project['author_id'])['username'],
        'total_images': db_manager.get_project_images_count(project_id),
        'classes': project['data'].get('classes', []),
        'progress': db_manager.get_project_progress_percent(project_id)
    }

    return jsonify({'project': project_info}), 200


# Join a project
@app.route('/api/projects/<int:project_id>/join', methods=['POST'])
@token_required
def join_project(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if current_user['id'] == project['author_id']:
        return jsonify({'message': 'You are the author of this project'}), 400

    if current_user['id'] in project['user_ids']:
        return jsonify({'message': 'You are already a participant in this project'}), 400

    project['user_ids'].append(current_user['id'])
    db_manager.update_project(project_id, user_ids=project['user_ids'])

    return jsonify({'message': 'You have joined the project'}), 200


# Leave a project
@app.route('/api/projects/<int:project_id>/leave', methods=['POST'])
@token_required
def leave_project(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    if current_user['id'] == project['author_id']:
        return jsonify({
            'message': 'You are the author of this project and cannot leave it. Consider deleting the project if you no longer need it.'}), 400

    if current_user['id'] not in project['user_ids']:
        return jsonify({'message': 'You are not a participant in this project'}), 400

    project['user_ids'].remove(current_user['id'])
    db_manager.update_project(project_id, user_ids=project['user_ids'])

    # Optionally, unassign images assigned to the user in this project
    db_manager.unassign_user_from_project_images(project_id, current_user['id'])

    return jsonify({'message': 'You have left the project'}), 200


@app.route('/api/projects/<int:project_id>/users', methods=['GET'])
@token_required
def get_project_users(current_user, project_id):
    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    # Only author can get the list of users
    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Access denied'}), 403

    user_stats = db_manager.get_project_users_stats(project_id)
    return jsonify({'users': user_stats}), 200


@app.route('/api/projects/<int:project_id>/kick_user', methods=['POST'])
@token_required
def kick_user_from_project(current_user, project_id):
    data = request.json
    user_id_to_kick = data.get('user_id')

    if not user_id_to_kick:
        return jsonify({'message': 'User ID is required'}), 400

    project = db_manager.get_project(project_id)
    if not project:
        return jsonify({'message': 'Project not found'}), 404

    # Only author can kick users
    if project['author_id'] != current_user['id']:
        return jsonify({'message': 'Access denied'}), 403

    user_id_to_kick = int(user_id_to_kick)

    if user_id_to_kick == project['author_id']:
        return jsonify({'message': 'Cannot kick the project owner'}), 400

    if user_id_to_kick not in project['user_ids']:
        return jsonify({'message': 'User not in project'}), 400

    # Remove user from project
    project['user_ids'].remove(user_id_to_kick)
    db_manager.update_project(project_id, user_ids=project['user_ids'])

    # Unassign images assigned to the user in this project
    db_manager.unassign_user_from_project_images(project_id, user_id_to_kick)

    return jsonify({'message': 'User has been kicked from the project'}), 200


#########################################################################################################
#########################################################################################################
#########################################################################################################

### Страницы

# Главная страница
@app.route('/main')
def index():
    return render_template('index.html')


# Страница логина и регистрации
@app.route('/login')
def login():
    return render_template('login.html')


# Страница аккаунта
@app.route('/account')
def account():
    return render_template('account.html')


@app.route('/new_project')
@token_required
def new_project(current_user):
    return render_template('new_project.html')


@app.route('/workspace')
@token_required
def workspace(current_user):
    project_id = request.args.get('project_id', type=int)
    if not project_id:
        return "Project ID is required", 400
    return render_template('workspace.html', project_id=project_id)


@app.route('/edit_project')
@token_required
def edit_project(current_user):
    project_id = request.args.get('project_id', type=int)
    if not project_id:
        return "Project ID is required", 400

    project = db_manager.get_project(project_id)
    if not project:
        return "Project not found", 404

    if project['author_id'] != current_user['id']:
        return "Access denied", 403

    return render_template('edit_project.html', project_id=project_id)

@app.route('/join')
@token_required
def join_project_page(current_user):
    return render_template('join.html')


if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0", port=8081)

```

Вот код js:
```js
// Helper function to convert hex to rgba
function hexToRgba(hex, alpha) {
    var r = parseInt(hex.slice(1, 3), 16),
        g = parseInt(hex.slice(3, 5), 16),
        b = parseInt(hex.slice(5, 7), 16);

    if (alpha) {
        return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
    } else {
        return "rgb(" + r + ", " + g + ", " + b + ")";
    }
}

$(document).ready(function () {
    var urlParams = new URLSearchParams(window.location.search);
    var projectId = urlParams.get('project_id');

    if (!projectId) {
        alert("Project ID is required.");
        window.location.href = '/main';
        return;
    }


    var currentImageId = null;
    var classes = [];
    var selectedClass = null;
    var isDrawing = false;
    var startX, startY;
    var annotationMode = false;

    var $imageContainer = $('.image-container');
    var imageContainerOffset;

    let crosshairVisible = false;
    let crosshairX = 0;
    let crosshairY = 0;

    var $contextMenu = $(".context-menu")
    // Function to show the context menu
    function showContextMenu(x, y, $annotation) {
        $contextMenu.css({
            left: x,
            top: y
        }).removeClass('hidden').data('annotation', $annotation); // Store the annotation element
    }

    // Function to hide the context menu
    function hideContextMenu() {
        $contextMenu.addClass('hidden').removeData('annotation');
    }

    // Context menu click handler
    $contextMenu.on('click', '.delete-annotation', function() {
        var $annotation = $contextMenu.data('annotation');
        if ($annotation) {
            $annotation.remove();
        }
        hideContextMenu();
    });

    // Annotation right-click handler
    $imageContainer.on('contextmenu', '.annotation', function(e) {
        e.preventDefault();

        if (annotationMode) {
          return;
        }

        var $annotation = $(this);
        showContextMenu(e.pageX, e.pageY, $annotation);


    });

    // Hide context menu on click outside
    $(document).on('click', function(e) {
        if (!$(e.target).closest('.context-menu').length) {
            hideContextMenu();
        }
    });

    // Prevent default image actions on image display
    $('#image-display').on('contextmenu', function(e) {
        e.preventDefault(); // Prevent context menu
    }).on('dragstart', function(e) {
        e.preventDefault(); // Prevent dragging
    });

    // Fetch account info
    AccountManager.getAccountInfo(function (error, data) {
        if (error) {
            window.location.href = '/login';
        } else {
            $('#username').text(data.username);
        }
    });

    // Fetch the image and project info
    $.ajax({
        url: '/api/get_image',
        type: 'GET',
        data: { project_id: projectId },
        success: function (response) {
            if (response.message) {
                alert(response.message);
                window.location.href = '/main';
                return;
            }

            $('#project-title').text(response.project_title || 'Project');
            $('#project-progress').val(response.progress);
            $('#progress-text').text('Image ' + response.current + ' of ' + response.total + ' (' + Math.round(response.progress) + '%)');

            // Set up classes
            classes = response.classes;
            var $classList = $('#class-list');
            $classList.empty();
            classes.forEach(function (cls, index) {
                var $classItem = $('<div class="class-item"></div>');
                $classItem.text(cls.name);
                $classItem.css('background-color', cls.color);
                $classItem.data('classIndex', index);
                $classList.append($classItem);
            });

            // Class selection handler
            $('.class-item').on('click', function () {
                selectedClass = classes[$(this).data('classIndex')];

                // Highlight selected class
                $('.class-item').removeClass('selected');
                $(this).addClass('selected');

                // Enter annotation mode
                annotationMode = true;
                $('#selected-class-name').text(selectedClass.name);
                $('#annotation-mode-indicator').removeClass('hidden');
                // Fade out existing annotations
                $('.annotation').css({
                    'opacity': 0.2,
                    'transition': 'opacity 0.3s ease'
                });
                $('.annotation').css('pointer-events', 'none');
            });

            // Load image
            currentImageId = response.image_id;
            var imageUrl = response.image_url;
            var $imageDisplay = $('#image-display');
            $imageDisplay.attr('src', imageUrl);

            // Set up image load handler
            $imageDisplay.on('load', function () {
                // Adjust image container dimensions
                $imageContainer.css({
                    'width': $imageDisplay.width(),
                    'height': $imageDisplay.height()
                });
                imageContainerOffset = $imageContainer.offset();
            });

            // Mouse events for drawing annotations
            $imageContainer.on('mousedown', function (e) {
                if (annotationMode && selectedClass) {
                    e.preventDefault();
                    isDrawing = true;
                    var offset = getMousePosition(e);
                    startX = offset.x;
                    startY = offset.y;

                    // Create a new annotation element
                    var $annotation = $('<div class="annotation"></div>');
                    $annotation.css({
                        'left': startX,
                        'top': startY,
                        'border-color': selectedClass.color,
                        'background-color': hexToRgba(selectedClass.color, 0.25), // Use the helper function
                    });
                    $annotation.data('class', selectedClass);
                    $annotation.append('<div class="delete-handle">&times;</div>');
                    $annotation.on('mousedown', annotationMouseDown);

                    // Добавляем угловые точки ресайза
                    $annotation.append('<div class="resize-handle nw"></div>');
                    $annotation.append('<div class="resize-handle ne"></div>'); // Add NE handle
                    $annotation.append('<div class="resize-handle sw"></div>');
                    $annotation.append('<div class="resize-handle se"></div>');

                    // События для перетаскивания угловых точек
                    $annotation.find('.resize-handle').on('mousedown', resizeMouseDown);

                    // Add to image container
                    $imageContainer.append($annotation);

                    // Attach delete handler
                    $annotation.find('.delete-handle').on('click', function (e) {
                        // Prevent deletion in annotation mode
                        if (annotationMode) {
                            return;
                        }
                        e.stopPropagation();
                        $(this).parent('.annotation').remove();
                    });

                    // Store current annotation element
                    $imageContainer.data('currentAnnotation', $annotation);
                }
            });

            $(document).on('mousemove', function (e) {
                if (isDrawing) {
                    var offset = getMousePosition(e);
                    var currentX = offset.x;
                    var currentY = offset.y;
                    var width = currentX - startX;
                    var height = currentY - startY;

                    var $annotation = $imageContainer.data('currentAnnotation');
                    $annotation.css({
                        'width': Math.abs(width),
                        'height': Math.abs(height),
                        'left': width < 0 ? currentX : startX,
                        'top': height < 0 ? currentY : startY
                    });
                }
                if (annotationMode && !isDrawing) {
                    crosshairX = e.clientX+2;
                    crosshairY = e.clientY+2;

                    $(".crosshairVertical").css("left", `${crosshairX}px`); // Update crosshair position
                    $(".crosshairHorizontal").css("top", `${crosshairY}px`); // Update crosshair position

                    if (!crosshairVisible) {
                        $(".crosshair").show();
                        crosshairVisible = true;
                    }
                } else {
                    $(".crosshair").hide();
                    crosshairVisible = false;
                }
            });

            $(document).on('mouseup', function (e) {
                if (isDrawing) {
                    isDrawing = false;
                    $imageContainer.removeData('currentAnnotation');

                    // Fade in existing annotations
                    $('.annotation').css({ // Exclude current
                        'opacity': 1,
                        'transition': 'opacity 0.3s ease'
                    });

                    // Re-enable pointer events after drawing is complete
                    $('.annotation').css('pointer-events', 'auto');

                    // После установки маркера автоматически выходим из режима установки
                    annotationMode = false;
                    $('#annotation-mode-indicator').addClass('hidden');
                    $('.class-item').removeClass('selected');
                    selectedClass = null;
                }
            });

            /////////////
            ////////

            // Функция для ресайза
            function resizeMouseDown(e) {
                e.stopPropagation();
                e.preventDefault();

                 if (annotationMode) {
                    return;
                }

                var $handle = $(this);
                var $annotation = $handle.parent();
                var startX = e.pageX;
                var startY = e.pageY;
                var startWidth = $annotation.width();
                var startHeight = $annotation.height();
                var startLeft = parseInt($annotation.css('left'));
                var startTop = parseInt($annotation.css('top'));

                $(document).on('mousemove.resize', function (e) {
                    var dx = e.pageX - startX;
                    var dy = e.pageY - startY;

                    if ($handle.hasClass('nw')) {
                        $annotation.css({
                            'left': startLeft + dx,
                            'top': startTop + dy,
                            'width': startWidth - dx,
                            'height': startHeight - dy
                        });
                    } else if ($handle.hasClass('ne')) {
                        $annotation.css({
                            'top': startTop + dy,
                            'width': startWidth + dx,
                            'height': startHeight - dy
                        });
                    } else if ($handle.hasClass('sw')) {
                        $annotation.css({
                            'left': startLeft + dx,
                            'width': startWidth - dx,
                            'height': startHeight + dy
                        });
                    } else if ($handle.hasClass('se')) {
                        $annotation.css({
                            'width': startWidth + dx,
                            'height': startHeight + dy
                        });
                    } else if ($handle.hasClass('ne')) { // Add resize logic for NE handle
                        $annotation.css({
                            'top': startTop + dy,
                            'width': startWidth + dx,
                            'height': startHeight - dy
                        });
                    }
                });

                $(document).on('mouseup.resize', function () {
                    $(document).off('.resize');
                });
            }
            //////
            ////////

            // Cancel annotation mode
            $('#cancel-annotation-btn').on('click', function () {
                // Fade in existing annotations
                $('.annotation').css({ // Exclude current
                    'opacity': 1,
                    'transition': 'opacity 0.3s ease'
                });
                $('.annotation').css('pointer-events', 'all');
                annotationMode = false;
                $('#annotation-mode-indicator').addClass('hidden');
                $('.class-item').removeClass('selected');
                selectedClass = null;
            });
        },
        error: function (xhr) {
            alert("An error occurred while fetching the image.");
            window.location.href = '/main';
        }
    });

    // Helper to get the mouse position relative to the image container
    // Обновление функции getMousePosition
    function getMousePosition(e) {
        var parentOffset = $imageContainer.offset();
        var x = e.pageX - parentOffset.left;
        var y = e.pageY - parentOffset.top;

        return { x: x, y: y };
    }

    // Annotation movement and resizing
    function annotationMouseDown(e) {
        if ($(e.target).hasClass('delete-handle')) {
            return; // Ignore delete handle clicks
        }
        e.stopPropagation();

        var $annotation = $(this);
        var startOffset = $annotation.offset();
        var startX = e.pageX;
        var startY = e.pageY;

        $(document).on('mousemove.annotation', function (e) {
            var dx = e.pageX - startX;
            var dy = e.pageY - startY;
            $annotation.css({
                'left': startOffset.left + dx - imageContainerOffset.left,
                'top': startOffset.top + dy - imageContainerOffset.top
            });
        });

        $(document).on('mouseup.annotation', function () {
            $(document).off('.annotation');
        });
    }

    // Next button click handler
    $('#next-btn').on('click', function () {
        // Collect annotation data
        var annotations = [];
        $('.annotation').each(function () {
            var $annotation = $(this);
            var cls = $annotation.data('class');
            var left = parseInt($annotation.css('left'));
            var top = parseInt($annotation.css('top'));
            var width = $annotation.width();
            var height = $annotation.height();

            // Normalize coordinates relative to image dimensions
            var imageWidth = $imageContainer.width();
            var imageHeight = $imageContainer.height();

            var x_center = (left + width / 2) / imageWidth;
            var y_center = (top + height / 2) / imageHeight;
            var w = width / imageWidth;
            var h = height / imageHeight;

            annotations.push({
                class_id: classes.indexOf(cls),
                x_center: x_center,
                y_center: y_center,
                width: w,
                height: h
            });
        });

        // Send annotations to server
        $.ajax({
            url: '/api/submit_annotation',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({
                project_id: projectId,
                image_id: currentImageId,
                annotation: annotations
            }),
            success: function (response) {
                // Reload the page for the next image
                location.reload();
            },
            error: function (xhr) {
                alert("An error occurred while submitting the annotation.");
            }
        });
    });
    // Следим за движением мыши над изображением
    $imageContainer.on('mousemove', function (e) {
        var mousePos = getMousePosition(e);

        // Для каждой аннотации проверяем расстояние до ручек ресайза
        $('.annotation').each(function () {
            var $annotation = $(this);
            var annotationOffset = $annotation.position();
            var annotationWidth = $annotation.width();
            var annotationHeight = $annotation.height();

            // Массив ручек ресайза
            var handles = [
                { name: 'nw', x: annotationOffset.left, y: annotationOffset.top },
                { name: 'ne', x: annotationOffset.left + annotationWidth, y: annotationOffset.top }, // Add NE handle to array
                { name: 'sw', x: annotationOffset.left, y: annotationOffset.top + annotationHeight },
                { name: 'se', x: annotationOffset.left + annotationWidth, y: annotationOffset.top + annotationHeight }
            ];

            // Проверяем каждую ручку
            $annotation.find('.resize-handle').each(function (index) {
                var $handle = $(this);
                var handlePos = handles[index];
                var distance = Math.hypot(mousePos.x - handlePos.x, mousePos.y - handlePos.y);

                if (distance <= 20) {
                    $handle.addClass('visible');
                } else {
                    $handle.removeClass('visible');
                }
            });
        });
    });

    // Когда курсор покидает область изображения, скрываем все ручки ресайза
    $imageContainer.on('mouseleave', function () {
        $('.resize-handle').removeClass('visible');
    });
});
```

Я хочу добавить кнопку "Bad image", которая будет отправлять запрос на специальный API эндпоинт, и это изображение удалится.
После этого покажется новое изображение, прямо как при нажатии на кнопку `#next-btn`.

На сервер уже есть эндпоинт на удаление изображение, но теперь нужен еще один, который был бы для обычных пользователей, не только для админов.

Также важно помнить про безопасность.