From df2991bdd12f5a8805bf4f9650110192a369657f Mon Sep 17 00:00:00 2001 From: charleswrayjr Date: Sun, 28 Sep 2025 23:41:54 -0500 Subject: [PATCH] Fixing file uploads for messages #PA-3. --- .idea/zencoder-chat-index.xml | 1 - src/controllers/files.controller.js | 8 +++++--- src/models/file.model.js | 23 ++++++++++++----------- src/models/index.js | 5 +++-- src/models/message.model.js | 4 +++- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.idea/zencoder-chat-index.xml b/.idea/zencoder-chat-index.xml index 28ae7b1..08dc41c 100755 --- a/.idea/zencoder-chat-index.xml +++ b/.idea/zencoder-chat-index.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/src/controllers/files.controller.js b/src/controllers/files.controller.js index d6b5cfa..2d619ac 100755 --- a/src/controllers/files.controller.js +++ b/src/controllers/files.controller.js @@ -29,20 +29,22 @@ module.exports = { try { const { visibility, specific_users } = req.body; const files = req.files || []; + const user_id = req.user.id; const results = []; for ( const file of files ) { const file_data = { user_id: req.user.id, - file_path: `/uploads/${ file.filename }`, + buffer: file.buffer, file_type: file.mimetype.startsWith( 'image' ) ? 'image' : 'video', + mimetype: file.mimetype, visibility: visibility || 'family', specific_users: specific_users ? JSON.parse( specific_users ) : [], created_by_id: req.user.id }; - const uploaded_file = await db.file.create( file_data ); + const uploaded_file = await db.file.create( file_data, user_id ); results.push( uploaded_file ); } - res.json( results.length === 1 ? results[ 0 ] : results ); + res.status(201).json( results.length === 1 ? results[ 0 ] : results ); } catch ( error ) { logger.error( `Create file error: ${ error.message }` ); next( createError( error.status || 409, error.message ) ); diff --git a/src/models/file.model.js b/src/models/file.model.js index 2c1a165..f5bd952 100755 --- a/src/models/file.model.js +++ b/src/models/file.model.js @@ -35,14 +35,15 @@ class File extends Model { /** @type {string} Table alias for queries */ this.prepend = 'f'; /** @type {string[]} Allowed columns for queries */ - this.default_columns = ['id', 'user_id', 'file_path', 'file_type', 'visibility', 'created_by_id', 'created_at', 'is_deleted', 'deleted_by_id', 'deleted_at']; + this.default_columns = ['id', 'user_id', 'file_data', 'mimetype', 'file_type', 'visibility', 'created_by_id', 'created_at', 'is_deleted', 'deleted_by_id', 'deleted_at']; /** @type {string[]} Columns excluded from updates */ this.update_exclude_columns = ['id', 'created_at', 'is_deleted', 'deleted_at', 'deleted_by_id']; /** @type {string} Base query for single record retrieval */ this.base_query = ` SELECT f.id, f.user_id, - f.file_path, + f.file_data, + f.mimetype, f.file_type, f.visibility, f.created_by_id, @@ -64,30 +65,30 @@ class File extends Model { /** * Create a new file - * @param {Omit & {specific_users?: number[]}} file_data - File data + * @param {Omit & {specific_users?: number[]}} file_data_ - File data + * @param {number|null} [created_by_id=null] - ID of user who created this file, defaults to user_id if not provided * @returns {Promise} Created file instance * @throws {ValidationError} If required fields are missing * @throws {FailedToCreateError} If creation fails */ - static async create( file_data ) { + static async create( file_data_, created_by_id = null ) { const { - user_id, - file_path, file_type, + buffer: file_data, + mimetype, visibility = 'family', - created_by_id = null, specific_users = [] - } = file_data; - if (!user_id || !file_path || !file_type) { + } = file_data_; + if (!created_by_id || !file_data || !file_type) { throw new ValidationError( 'Missing required fields: user_id, file_path, file_type' ); } return await this.prototype.with_transaction( async ( client ) => { const query_str = ` - INSERT INTO phase.files (user_id, file_path, file_type, visibility, created_by_id) + INSERT INTO phase.files (user_id, file_data, mimetype, file_type, visibility, created_by_id) VALUES ($1, $2, $3, $4, $5) RETURNING *; `; - const values = [user_id, file_path, file_type, visibility, created_by_id || user_id]; + const values = [created_by_id, file_data, mimetype, file_type, visibility, created_by_id]; const result = await phsdb.client_query( client, query_str, values, { plain:true } ); if (!result) throw new FailedToCreateError( 'Failed to create file' ); const file = new File( result ); diff --git a/src/models/index.js b/src/models/index.js index 635ff5b..7ce883e 100755 --- a/src/models/index.js +++ b/src/models/index.js @@ -841,12 +841,13 @@ const db = { /** * Create a new file * @param {Object} file_data - File data + * @param {number} created_by_id - ID of user performing creation * @returns {Promise} Created file instance * @throws {Error} If creation fails */ - async create( file_data ) { + async create( file_data, created_by_id ) { try { - return await get_file_model().create( file_data ); + return await get_file_model().create( file_data, created_by_id ); } catch (error) { logger.error( `Failed to create file: ${ error.message }` ); throw error; diff --git a/src/models/message.model.js b/src/models/message.model.js index 04ca92f..00c0f18 100755 --- a/src/models/message.model.js +++ b/src/models/message.model.js @@ -51,8 +51,9 @@ class Message extends Model { concat(se.first_name, ' ', se.last_name) as sender_name, concat(re.first_name, ' ', re.last_name) as recipient_name, concat(mg.name) as group_name, - (SELECT json_agg(json_build_object('file_id', mf.file_id)) + (SELECT json_agg(json_build_object('id', f.id, 'file_type', f.file_type, 'file_data', encode(f.file_data, 'base64'), 'mimetype', f.mimetype)) FROM phase.message_files mf + inner join phase.files f on f.id = mf.file_id WHERE mf.message_id = m.id) as attached_files, (SELECT json_agg(json_build_object('user_id', mr.user_id, 'reaction', mr.reaction)) FROM phase.message_reactions mr @@ -99,6 +100,7 @@ class Message extends Model { await phsdb.client_query( client, file_query, [message.id, file_id] ); } } + message.attached_files = await phsdb.client_query( client, `select id, file_type, encode(file_data, 'base64'), mimetype from phase.files where id in (${file_ids.join(',')})`, [], { plain:true } ); return message; } ); } -- 2.43.0