/
kalugin66
/
FileBridge
Обзор
Документация
Войти
/
kalugin66
/
FileBridge
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
restapi.js
500 строк
20 KB
kalugin66
json
25 янв 2026, 11:35
25 янв 2026, 11:35
b78da05
Код
Авторство
О чём код?
const multer = require('multer'); const path = require('path'); const { createClient } = require('webdav'); const crypto = require('crypto'); module.exports = function(app, checkApiToken) { // Настройка multer для обработки файлов const storage = multer.memoryStorage(); const upload = multer({ storage: storage, limits: { fileSize: parseInt(process.env.UPLOAD_MAX_SIZE) || 910485760 } }); // Конфигурация WebDAV const webdavClient = createClient( process.env.webdav_url, { username: process.env.webdav_login, password: process.env.webdav_password } ); const db = require('./postgresql.js'); // Счетчик для номеров файлов let currentFileNumber = 90000000000000000n; // Инициализация счетчика (async () => { try { const result = await db.query( 'SELECT MAX(file_number) as max_number FROM files' ); if (result.rows[0] && result.rows[0].max_number) { currentFileNumber = BigInt(result.rows[0].max_number); console.log(`Current file number initialized to: ${currentFileNumber}`); } } catch (err) { console.error('Error initializing file counter:', err); } })(); // Генерация следующего номера файла function getNextFileNumber() { currentFileNumber++; return currentFileNumber.toString(); } // Создание структуры папок в WebDAV async function ensureWebDAVStructure(serviceName, date) { const basePath = process.env.webdav_fs; const filebridge = `${basePath}/filebridge`; const servicePath = `${filebridge}/${serviceName}`; const datePath = `${servicePath}/${date}`; try { // Проверяем и создаем папку filebridge if (!await webdavClient.exists(filebridge)) { await webdavClient.createDirectory(filebridge); } // Проверяем и создаем папку сервиса if (!await webdavClient.exists(servicePath)) { await webdavClient.createDirectory(servicePath); } // Проверяем и создаем папку даты if (!await webdavClient.exists(datePath)) { await webdavClient.createDirectory(datePath); } return datePath; } catch (err) { throw new Error(`WebDAV error: ${err.message}`); } } // Создание JSON метаинформации async function createMetaJsonFile(fileData, webdavFilePath) { try { // Подготавливаем данные для JSON const metaData = { file_number: fileData.file_number, original_filename: fileData.original_filename, extension: fileData.extension, file_size: fileData.file_size, upload_date: fileData.upload_date, service_name: fileData.service_name, login: fileData.login, description: fileData.description, uploaded_at: new Date().toISOString(), metadata_created_at: new Date().toISOString() }; // Создаем JSON строку const jsonContent = JSON.stringify(metaData, null, 2); // Определяем путь для JSON файла const jsonFilePath = webdavFilePath + '.json'; // Сохраняем JSON файл в WebDAV await webdavClient.putFileContents(jsonFilePath, jsonContent, { overwrite: true }); console.log(`Meta JSON created: ${jsonFilePath}`); return true; } catch (err) { console.error('Error creating meta JSON:', err); return false; } } // API для админ-панели - пользователи app.get('/api/admin/users', checkApiToken, async (req, res) => { try { const users = await db.getAllUsers(); res.json(users); } catch (err) { res.status(500).json({ error: err.message }); } }); app.get('/api/admin/users/:id', checkApiToken, async (req, res) => { try { const result = await db.query( 'SELECT id, login, email, is_admin, created_at, last_login FROM users WHERE id = $1', [req.params.id] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'User not found' }); } res.json(result.rows[0]); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/admin/users', checkApiToken, async (req, res) => { try { const { login, email, password, is_admin } = req.body; if (!login || !email || !password) { return res.status(400).json({ error: 'Missing required fields' }); } const bcrypt = require('bcryptjs'); const hashedPassword = await bcrypt.hash(password, 10); const result = await db.createUser(login, hashedPassword, email, is_admin); // Отправляем email уведомление const mail = require('./mail.js'); await mail.sendUserCreatedNotification(email, login, password, is_admin); res.json({ success: true, user: result }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/admin/users/:id', checkApiToken, async (req, res) => { try { const { login, email, password, is_admin } = req.body; const updateData = {}; if (login !== undefined) updateData.login = login; if (email !== undefined) updateData.email = email; if (is_admin !== undefined) updateData.is_admin = is_admin; // Хешируем пароль если он предоставлен if (password) { const bcrypt = require('bcryptjs'); updateData.password = await bcrypt.hash(password, 10); } const updatedUser = await db.updateUser(req.params.id, updateData); res.json({ success: true, user: updatedUser }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/admin/users/:id', checkApiToken, async (req, res) => { try { await db.deleteUser(req.params.id); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Загрузка файла app.post('/api/upload', checkApiToken, upload.single('file'), async (req, res) => { try { console.log('Upload request received'); console.log('Body:', req.body); console.log('File:', req.file); const { service_name, login, description } = req.body; if (!req.file) { return res.status(400).json({ error: 'No file uploaded' }); } if (!service_name || !login) { return res.status(400).json({ error: 'Missing service_name or login' }); } const file = req.file; const fileNumber = getNextFileNumber(); const today = new Date().toISOString().split('T')[0]; const extension = path.extname(file.originalname).toLowerCase().replace('.', '') || 'bin'; const filename = `${fileNumber}.${extension}`; console.log(`File info: number=${fileNumber}, extension=${extension}, size=${file.size}`); // Создаем структуру папок const uploadPath = await ensureWebDAVStructure(service_name, today); const webdavFilePath = `${uploadPath}/${filename}`; console.log(`Uploading to WebDAV: ${webdavFilePath}`); // Загружаем файл в WebDAV await webdavClient.putFileContents(webdavFilePath, file.buffer); // Сохраняем метаданные в базу const fileData = { service_name, login, description: description || '', file_number: fileNumber, original_filename: file.originalname, extension: extension, file_size: file.size, upload_date: today }; // Сохраняем JSON метаинформацию console.log(`Сохраняем JSON метаинформацию`); await createMetaJsonFile(fileData, webdavFilePath); console.log('Saving file metadata:', fileData); const savedFile = await db.saveFile(fileData); // Отправляем уведомление на почту если есть email try { const user = await db.getUserByLogin(login); if (user && user.email) { const mail = require('./mail.js'); await mail.sendFileUploadNotification( user.email, file.originalname, fileNumber, file.size ); } } catch (mailErr) { console.error('Error sending email notification:', mailErr); } res.json({ success: true, file_number: fileNumber, filename: file.originalname, size: file.size, uploaded_at: savedFile.uploaded_at }); } catch (err) { console.error('Upload error:', err); res.status(500).json({ error: err.message }); } }); // Список файлов по логину app.get('/api/files/:login', checkApiToken, async (req, res) => { try { const files = await db.getFilesByLogin(req.params.login); res.json(files); } catch (err) { console.error('Error getting files by login:', err); res.status(500).json({ error: err.message }); } }); function formatDateToYYYYMMDD(dateString) { if (!dateString) return null; // Если уже в правильном формате if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) { return dateString; } // Парсим строку в Date объект const date = new Date(dateString); // Проверяем что дата валидна if (isNaN(date.getTime())) { console.error('Invalid date:', dateString); return 'invalid-date'; } // Форматируем в YYYY-MM-DD const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } // Скачивание файла app.get('/api/download/:file_number', checkApiToken, async (req, res) => { try { console.log(`=== Запрос на скачивание файла ${req.params.file_number} ===`); const file = await db.getFileByNumber(req.params.file_number); const uploadDate = formatDateToYYYYMMDD(file.upload_date); if (!file) { console.log(`Файл ${req.params.file_number} не найден в базе данных`); return res.status(404).json({ error: 'File not found in database' }); } const filePath = `${process.env.webdav_fs}/filebridge/${file.service_name}/${uploadDate}/${file.file_number}.${file.extension}`; console.log(`upload_date: ${uploadDate}`); console.log(`WebDAV путь: ${filePath}`); // Проверяем существование файла в WebDAV try { const exists = await webdavClient.exists(filePath); if (!exists) { console.log(`Файл не найден в WebDAV: ${filePath}`); return res.status(404).json({ error: 'File not found in storage' }); } } catch (checkErr) { console.error('Ошибка проверки существования файла:', checkErr.message); return res.status(500).json({ error: 'Storage check failed' }); } // Получаем информацию о файле let fileSize = 0; try { const stat = await webdavClient.stat(filePath); fileSize = stat.size; console.log(`Размер файла в WebDAV: ${fileSize} байт`); } catch (statErr) { console.error('Ошибка получения информации о файле:', statErr.message); // Продолжаем без информации о размере } // Настраиваем заголовки ответа res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(file.original_filename)}"`); res.setHeader('Content-Type', 'application/octet-stream'); if (fileSize > 0) { res.setHeader('Content-Length', fileSize); } // Получаем файл как stream try { const readStream = webdavClient.createReadStream(filePath); // Отправляем файл клиенту readStream.pipe(res); // Обработка ошибок stream readStream.on('error', (streamErr) => { console.error('Ошибка при чтении файла из WebDAV:', streamErr.message); if (!res.headersSent) { res.status(500).json({ error: 'Error reading file from storage' }); } else { res.end(); } }); // Логирование завершения readStream.on('end', () => { console.log(`Файл ${req.params.file_number} успешно отправлен клиенту`); }); } catch (streamErr) { console.error('Ошибка создания read stream:', streamErr.message); if (!res.headersSent) { res.status(500).json({ error: 'Error creating read stream' }); } } } catch (err) { console.error('Ошибка при скачивании файла:', err); if (!res.headersSent) { res.status(500).json({ error: err.message }); } } }); // Получение всех файлов (с фильтрацией) app.get('/api/files', checkApiToken, async (req, res) => { try { const { service, date, extension, limit = 100, offset = 0 } = req.query; const files = await db.getAllFiles(service, date, extension, parseInt(limit), parseInt(offset)); const total = await db.countFiles(service, date, extension); res.json({ files, pagination: { total, limit: parseInt(limit), offset: parseInt(offset), hasMore: (parseInt(offset) + files.length) < total } }); } catch (err) { console.error('Error getting all files:', err); res.status(500).json({ error: err.message }); } }); // Получение информации о файле app.get('/api/file/:file_number', checkApiToken, async (req, res) => { try { const file = await db.getFileByNumber(req.params.file_number); if (!file) { return res.status(404).json({ error: 'File not found' }); } res.json(file); } catch (err) { res.status(500).json({ error: err.message }); } }); // Удаление файла app.delete('/api/file/:file_number', checkApiToken, async (req, res) => { try { const file = await db.getFileByNumber(req.params.file_number); const uploadDate = formatDateToYYYYMMDD(file.upload_date); if (!file) { return res.status(404).json({ error: 'File not found' }); } const filePath = `${process.env.webdav_fs}/filebridge/${file.service_name}/${uploadDate}/${file.file_number}.${file.extension}`; // Удаляем файл из WebDAV await webdavClient.deleteFile(filePath); // Удаляем запись из базы await db.query('DELETE FROM files WHERE file_number = $1', [req.params.file_number]); // Удаляем JSON файл метаинформации const jsonFilePath = filePath + '.json'; try { if (await webdavClient.exists(jsonFilePath)) { await webdavClient.deleteFile(jsonFilePath); console.log(`Meta JSON deleted: ${jsonFilePath}`); } } catch (jsonErr) { console.error('Error deleting meta JSON:', jsonErr); // Продолжаем удаление основного файла даже если JSON не удалился } res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Статистика app.get('/api/stats', checkApiToken, async (req, res) => { try { const totalFiles = await db.query('SELECT COUNT(*) FROM files'); const totalSize = await db.query('SELECT SUM(file_size) FROM files'); const byService = await db.query('SELECT service_name, COUNT(*) as count, SUM(file_size) as size FROM files GROUP BY service_name'); const byDate = await db.query('SELECT upload_date, COUNT(*) as count FROM files GROUP BY upload_date ORDER BY upload_date DESC LIMIT 30'); res.json({ total_files: parseInt(totalFiles.rows[0].count || 0), total_size: parseInt(totalSize.rows[0].sum || 0), by_service: byService.rows.map(row => ({ service_name: row.service_name, count: parseInt(row.count || 0), size: parseInt(row.size || 0) })), recent_dates: byDate.rows.map(row => ({ upload_date: row.upload_date, count: parseInt(row.count || 0) })) }); } catch (err) { console.error('Error getting stats:', err); res.status(500).json({ error: err.message }); } }); };