import { pool } from '../db.js'; /** * Repository для работы с метаданными файлов пользователей */ export const userFileRepository = { /** * Создать запись о файле */ async create({ userId, s3Key, originalFilename, fileSize, contentType, fileType = 'image', folder = 'images_input', generationUuid = null, generationStepId = null, status = 'uploaded', uploadId = null }) { const query = ` INSERT INTO uno_bff.user_files (user_id, s3_key, original_filename, file_size, content_type, file_type, folder, generation_uuid, generation_step_id, status, upload_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, generation_uuid, generation_step_id, status, upload_id, created_at, updated_at `; const values = [userId, s3Key, originalFilename, fileSize, contentType, fileType, folder, generationUuid, generationStepId, status, uploadId]; const result = await pool.query(query, values); return result.rows[0]; }, /** * Обновить файл */ async update(fileId, updates) { const allowedFields = ['status', 'upload_id', 'file_size', 'updated_at']; const setClauses = []; const values = [fileId]; let paramIndex = 2; for (const [key, value] of Object.entries(updates)) { if (allowedFields.includes(key)) { setClauses.push(`${key} = $${paramIndex}`); values.push(value); paramIndex++; } } if (setClauses.length === 0) { throw new Error('No valid fields to update'); } const query = ` UPDATE uno_bff.user_files SET ${setClauses.join(', ')} WHERE id = $1 RETURNING id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, status, upload_id, created_at, updated_at `; const result = await pool.query(query, values); return result.rows[0]; }, /** * Удалить файл по ID */ async delete(fileId) { const query = ` DELETE FROM uno_bff.user_files WHERE id = $1 RETURNING id `; const result = await pool.query(query, [fileId]); return result.rows[0]; }, /** * Найти файл по ID и проверить владение */ async findByIdAndOwner(fileId, userId) { const query = ` SELECT id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, created_at FROM uno_bff.user_files WHERE id = $1 AND user_id = $2 `; const result = await pool.query(query, [fileId, userId]); return result.rows[0]; }, /** * Найти файл по S3 ключу и проверить владение */ async findByKeyAndOwner(s3Key, userId) { const query = ` SELECT id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, created_at FROM uno_bff.user_files WHERE s3_key = $1 AND user_id = $2 `; const result = await pool.query(query, [s3Key, userId]); return result.rows[0]; }, /** * Получить список файлов пользователя */ async findByUser(userId, options = {}) { const { fileType = 'image', folder = 'images_input', limit = 50, offset = 0, orderBy = 'created_at', order = 'DESC', } = options; const validOrderColumns = ['created_at', 'original_filename', 'file_size']; const validOrders = ['ASC', 'DESC']; const safeOrderBy = validOrderColumns.includes(orderBy) ? orderBy : 'created_at'; const safeOrder = validOrders.includes(order?.toUpperCase()) ? order.toUpperCase() : 'DESC'; const query = ` SELECT id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, created_at FROM uno_bff.user_files WHERE user_id = $1 AND ($2::VARCHAR IS NULL OR file_type = $2) AND ($3::VARCHAR IS NULL OR folder = $3) ORDER BY ${safeOrderBy} ${safeOrder} LIMIT $4 OFFSET $5 `; const values = [ userId, fileType || null, folder || null, limit, offset, ]; const result = await pool.query(query, values); return result.rows; }, /** * Получить общее количество файлов пользователя */ async countByUser(userId, options = {}) { const { fileType = 'image', folder = 'images_input' } = options; const query = ` SELECT COUNT(*) as total FROM uno_bff.user_files WHERE user_id = $1 AND ($2::VARCHAR IS NULL OR file_type = $2) AND ($3::VARCHAR IS NULL OR folder = $3) `; const values = [userId, fileType || null, folder || null]; const result = await pool.query(query, values); return parseInt(result.rows[0].total, 10); }, /** * Удалить запись о файле (по ID и владельцу) */ async deleteByIdAndOwner(fileId, userId) { const query = ` DELETE FROM uno_bff.user_files WHERE id = $1 AND user_id = $2 RETURNING s3_key `; const result = await pool.query(query, [fileId, userId]); return result.rows[0]; }, /** * Удалить запись о файле по S3 ключу */ async deleteByKeyAndOwner(s3Key, userId) { const query = ` DELETE FROM uno_bff.user_files WHERE s3_key = $1 AND user_id = $2 RETURNING s3_key `; const result = await pool.query(query, [s3Key, userId]); return result.rows[0]; }, /** * Получить файлы генерации */ async findByGeneration(generationUuid, userId) { const query = ` SELECT id, user_id, s3_key, original_filename, file_size, content_type, file_type, folder, generation_uuid, generation_step_id, created_at FROM uno_bff.user_files WHERE generation_uuid = $1 AND user_id = $2 ORDER BY created_at ASC `; const result = await pool.query(query, [generationUuid, userId]); return result.rows; }, /** * Получить файлы шага генерации */ async findByGenerationStep(generationUuid, stepId, userId) { const query = ` SELECT f.id, f.user_id, f.s3_key, f.original_filename, f.file_size, f.content_type, f.file_type, f.folder, f.generation_uuid, f.generation_step_id, f.created_at FROM uno_bff.user_files f INNER JOIN uno_bff.generation_steps gs ON f.generation_step_id = gs.id WHERE f.generation_uuid = $1 AND gs.step_id = $2 AND f.user_id = $3 ORDER BY f.created_at ASC `; const result = await pool.query(query, [generationUuid, stepId, userId]); return result.rows; }, };