Файл отправляется, даже появляется на файлообменнике, но в ответ приходит ошибка получения POST

делаю сайт, в котором можно будет обмениваться модами, сейчас работаю над тем, чтобы файл посылался на файлообменник, а оттуда приходил его id и вместе с другими данными отправлялся, остановился на том, что уже 3 дня пытаюсь решить проблему, как я понимаю CORS, то есть, файл отправляется, даже появляется на файлообменнике, но в ответ приходит ошибка получения POST. Сам CORS реагирует ТОЛЬКО на архив, что я отправляю на TransferNow.

Если я правильно все понял, то проблема с тем, как я настроил сервер, но что бы я не читал, что бы не предпринимал, ошибка не уходит. Сама ошибка:

XHR не удалось загрузить:  POST "  http://localhost:3000/proxy/upload
uploadToTransferNow @ Upload.js:100
Выполнен переход к ...

Вот что пишет во вкладке сети в браузере:

URL-адрес запроса:
http://localhost:3000/proxy/upload
Политика источника ссылки:
strict-origin-when-cross-origin
accept:
*/*
accept-encoding:
gzip, deflate, br, zstd
accept-language:
ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7,uk;q=0.6
connection:
keep-alive
content-length:
459382
content-type:
multipart/form-data; boundary=----WebKitFormBoundaryjAQljaDPreQcoyRb
host:
localhost:3000
origin:
http://192.168.1.143:5500
referer:
http://192.168.1.143:5500/
sec-ch-ua:
"Not A(Brand";v="8", "Chromium";v="132", "Microsoft Edge";v="132"
sec-ch-ua-mobile:
?0
sec-ch-ua-platform:
"Windows"
sec-fetch-dest:
empty
sec-fetch-mode:
cors
sec-fetch-site:
cross-site
user-agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0

Вот код Upload.js

document.addEventListener('DOMContentLoaded', () => {
    const uploadForm = document.getElementById('upload-form');
    const logoInput = document.getElementById('logo');
    const modFileInput = document.getElementById('mod-file');
    const screenshotsInput = document.getElementById('screenshots');
    const logoContainer = document.getElementById('logo-container');
    const screenshotsContainer = document.getElementById('screenshots-container');
    const logoProgress = document.getElementById('logo-progress');
    const screenshotsProgress = document.getElementById('screenshots-progress');
    const logoThumbnails = document.getElementById('logo-thumbnails');
    const screenshotsThumbnails = document.getElementById('screenshots-thumbnails');
    let screenshotsUrls = [];
    let deleteHashes = [];

    function handleDragOver(event) {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'copy'; // Указываем, что перетаскиваемый объект будет скопирован
        event.currentTarget.classList.add('drag-over');
        console.log('Drag over');
    }

    function handleDragLeave(event) {
        event.currentTarget.classList.remove('drag-over');
        console.log('Drag leave');
    }

    function handleDrop(event, inputElement, maxFiles) {
        event.preventDefault();
        event.currentTarget.classList.remove('drag-over');
        if (event.dataTransfer.files.length > maxFiles) {
            alert(`Можно загрузить не более ${maxFiles} файлов.`);
            return;
        }
        inputElement.files = event.dataTransfer.files;
        event.currentTarget.classList.add('has-file');
        console.log('Files dropped');
        handleFileUpload(inputElement, event.currentTarget.querySelector('.progress-bar'), event.currentTarget.querySelector('.thumbnails'));
    }

    async function handleFileUpload(inputElement, progressBar, thumbnailsContainer) {
        const files = inputElement.files;
        if (thumbnailsContainer) {
            thumbnailsContainer.innerHTML = '';
            Array.from(files).forEach(file => {
                const reader = new FileReader();
                reader.onload = (e) => {
                    const img = document.createElement('img');
                    img.src = e.target.result;
                    thumbnailsContainer.appendChild(img);
                };
                reader.readAsDataURL(file);
            });
        }
        progressBar.style.width = '100%';
        setTimeout(() => {
            progressBar.style.width = '0%';
        }, 500);
        console.log('File uploaded');
    }

    [logoContainer, screenshotsContainer].forEach(container => {
        const maxFiles = container === screenshotsContainer ? 3 : 1;
        container.addEventListener('dragover', handleDragOver);
        container.addEventListener('dragleave', handleDragLeave);
        container.addEventListener('drop', (event) => handleDrop(event, container.querySelector('input[type="file"]'), maxFiles));
    });

    logoInput.addEventListener('change', () => {
        if (logoInput.files.length > 1) {
            alert('Можно загрузить только один логотип.');
            logoInput.value = '';
            return;
        }
        handleFileUpload(logoInput, logoProgress, logoThumbnails);
    });

    screenshotsInput.addEventListener('change', () => {
        if (screenshotsInput.files.length > 3) {
            alert('Можно загрузить не более 3 скриншотов.');
            screenshotsInput.value = '';
            return;
        }
        handleFileUpload(screenshotsInput, screenshotsProgress, screenshotsThumbnails);
    });

    modFileInput.addEventListener('change', () => {
        if (modFileInput.files.length > 1) {
            alert('Можно загрузить только один файл мода.');
            modFileInput.value = '';
            return;
        }
        console.log('Файл мода выбран:', modFileInput.files[0]);
    });

    async function uploadToTransferNow(file) {
        console.log('Начало загрузки на TransferNow');
        const formData = new FormData();
        formData.append('file', file);

        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'http://localhost:3000/proxy/upload'); // Используем прокси-сервер

            xhr.upload.onprogress = (event) => {
                if (event.lengthComputable) {
                    const percentComplete = (event.loaded / event.total) * 100;
                    console.log(`Upload progress: ${percentComplete}%`);
                }
            };

            xhr.onload = () => {
                if (xhr.status === 200) {
                    const response = JSON.parse(xhr.responseText);
                    console.log('Загрузка на TransferNow завершена успешно:', response);
                    resolve(response.transferLink);
                } else {
                    console.error('Ошибка загрузки на TransferNow:', xhr.statusText);
                    reject(new Error(`Ошибка загрузки на TransferNow: ${xhr.statusText}`));
                }
            };

            xhr.onerror = () => {
                console.error('Ошибка сети при загрузке на TransferNow');
                reject(new Error('Ошибка сети'));
            };

            xhr.send(formData);
            console.log('Запрос на загрузку отправлен');
        });
    }
    async function uploadToImgur(file, progressBar) {
        console.log('Начало загрузки на Imgur');
        const formData = new FormData();
        formData.append('image', file);

        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', 'https://api.imgur.com/3/image');
            xhr.setRequestHeader('Authorization', 'Client-ID a7e1b05ddeeed49');

            xhr.upload.onprogress = (event) => {
                if (event.lengthComputable) {
                    const percentComplete = (event.loaded / event.total) * 100;
                    progressBar.style.width = `${percentComplete}%`;
                    console.log(`Upload progress: ${percentComplete}%`);
                }
            };

            xhr.onload = () => {
                if (xhr.status === 200) {
                    const response = JSON.parse(xhr.responseText);
                    console.log('Загрузка на Imgur завершена успешно:', response);
                    resolve(response.data.link);
                } else {
                    console.error('Ошибка загрузки на Imgur:', xhr.statusText);
                    reject(new Error(`Ошибка загрузки на Imgur: ${xhr.statusText}`));
                }
            };

            xhr.onerror = () => {
                console.error('Ошибка сети при загрузке на Imgur');
                reject(new Error('Ошибка сети'));
            };

            xhr.send(formData);
            console.log('Запрос на загрузку отправлен');
        });
    }

    async function uploadToImgurWithRetry(file, progressBar, retries = 5) {
        try {
            return await uploadToImgur(file, progressBar);
        } catch (error) {
            if (retries > 0) {
                console.warn(`Ошибка загрузки на Imgur, повторная попытка... (${retries})`);
                return await uploadToImgurWithRetry(file, progressBar, retries - 1);
            } else {
                throw error;
            }
        }
    }

    async function deleteFromImgur(deletehash) {
        console.log('Начало удаления с Imgur');
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('DELETE', `https://api.imgur.com/3/image/${deletehash}`);
            xhr.setRequestHeader('Authorization', 'Client-ID ###');

            xhr.onload = () => {
                if (xhr.status === 200) {
                    console.log('Удаление с Imgur завершено успешно');
                    resolve();
                } else {
                    console.error('Ошибка удаления с Imgur:', xhr.statusText);
                    reject(new Error(`Ошибка удаления с Imgur: ${xhr.statusText}`));
                }
            };

            xhr.onerror = () => {
                console.error('Ошибка сети при удалении с Imgur');
                reject(new Error('Ошибка сети'));
            };

            xhr.send();
            console.log('Запрос на удаление отправлен');
        });
    }

    uploadForm.addEventListener('submit', async (event) => {
        event.preventDefault();
        event.stopPropagation();
        console.log('Форма отправлена');

        const formData = new FormData(uploadForm);

        const logoFile = formData.get('logo');
        const modFile = formData.get('mod-file');

        if (!logoFile || !modFile) {
            console.error('Файл не был загружен');
            alert('Пожалуйста, загрузите файл перед отправкой формы.');
            return;
        }

        try {
            console.log('Загрузка логотипа на Imgur');
            const logoUrl = await uploadToImgurWithRetry(logoFile, logoProgress);
            console.log('Логотип загружен:', logoUrl);

            console.log('Загрузка мод-файла на TransferNow');
            const modFileUrl = await uploadToTransferNow(modFile);
            console.log('Мод-файл загружен:', modFileUrl);

            const modData = {
                name: formData.get('name'),
                description: formData.get('description').replace(/\n/g, '<br>'), // Сохранение переносов строк
                short_description: formData.get('opis'), // Извлечение short_description из инпута "opis"
                game: formData.get('game'),
                logo_url: logoUrl,
                screenshots: screenshotsUrls,
                mod_file_url: modFileUrl
            };

            console.log('Данные для отправки на сервер:', modData);

            console.log('Отправка данных на сервер');
            const response = await fetch('http://localhost:3000/upload', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(modData),
            });

            const result = await response.json();
            if (response.ok) {
                console.log('Мод успешно загружен:', result);
                alert('Мод успішно завантажено!');
            } else {
                console.error('Ошибка при загрузке мода:', result.error);
                alert(result.error || 'Щось пішло не так');
            }
        } catch (error) {
            console.error('Ошибка на сервере:', error);
            alert('Помилка на сервері');
        }
    });
});

Так же вот код index.js, через который проходит отправка архива на файлообменни:

const express = require('express');
const { Pool } = require('pg');
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');
const cors = require('cors'); // Импортируем пакет cors
const multer = require('multer'); // Импортируем multer для обработки загрузки файлов
const { upload } = require('@transfernow/api-sdk'); // Импортируем библиотеку TransferNow
const path = require('path'); // Импортируем path для работы с путями файлов

// Настройка Express
const app = express();
app.use(bodyParser.json()); // Для обработки JSON в теле запроса
app.use(cors()); // Используем cors middleware

const pool = new Pool({
  user: 'postgres', // Замените на имя вашего пользователя БД
  host: 'localhost',
  database: 'Users', // Замените на имя вашей базы данных
  password: '1012', // Замените на пароль вашей базы данных
  port: 5432,
});

// Настройка multer для обработки загрузки файлов
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  }
});
const uploadMiddleware = multer({ storage: storage });

// Обработчик для корневого URL
app.get('/', (req, res) => {
  res.send('Сервер работает!');
});

// Обработка загрузки мода
app.post('/upload', async (req, res) => {
  const { name, description, short_description, game, logo_url, screenshots, mod_file_url } = req.body;

  console.log('Полученные данные для загрузки мода:', req.body);

  try {
    const tableName = `public."${game}_Mods"`;
    const result = await pool.query(
      `INSERT INTO ${tableName} (name, description, short_description, logo_url, screenshots, mod_file_url) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
      [name, description, short_description, logo_url, JSON.stringify(screenshots), mod_file_url]
    );

    console.log('Мод успешно загружен:', result.rows[0]);
    res.status(201).json(result.rows[0]);
  } catch (error) {
    console.error('Ошибка на сервере:', error);
    res.status(500).json({ error: 'Помилка на сервері' });
  }
});

// Прокси для загрузки на TransferNow
app.post('/proxy/upload', uploadMiddleware.single('file'), async (req, res) => {
  try {
    if (!req.file) {
      throw new Error('Файл не был загружен');
    }

    console.log('Загруженный файл:', req.file);

    const response = await upload({
      apiKey: '###',
      toEmails: ['[email protected]'],
      filePaths: [req.file.path],
      message: 'File upload via proxy',
      subject: 'File Upload'
    });

    const transferLink = `https://www.transfernow.net/dl/${response.transferId}`;
    console.log(`Transfer ${response.transferId} was sent successfully`);
    console.log(`Transfer link: ${transferLink}`);

    // Отправляем ссылку обратно клиенту
    res.json({ transferLink });
  } catch (error) {
    console.error('Ошибка при загрузке на TransferNow:', error.message);
    res.status(500).json({ error: 'Ошибка при загрузке на TransferNow' });
  }
});

// Запуск сервера
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

Ответы (1 шт):

Автор решения: Виктор Карев

Современные браузеры стремятся запретить получать или отправлять данные из сторонних источников в обход пользователя.

Попробуйте генерировать кнопку с адресом скачивания. По нажатию на кнопку файл должен скачаться локально и только после этого его можно будет обработать.

Другой вариант - отдайте пользователю ссылку на файлообменник, а файл, если его нужно показать тут же, отправьте через свой сервер.

→ Ссылка