]> PHS Git Server - phs-api.git/commitdiff
Fixing file uploads for messages #PA-3. master
authorcharleswrayjr <charleswrayjr@gmail.com>
Mon, 29 Sep 2025 04:41:54 +0000 (23:41 -0500)
committercharleswrayjr <charleswrayjr@gmail.com>
Mon, 29 Sep 2025 04:41:54 +0000 (23:41 -0500)
.idea/zencoder-chat-index.xml
src/controllers/files.controller.js
src/models/file.model.js
src/models/index.js
src/models/message.model.js

index 28ae7b1300d38f80228c92fe6446c4e14765ead4..08dc41ce9696187a52a802c3f5bef95a87f84d45 100755 (executable)
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="ai.zencoder.plugin.chat.index">
-    <option name="activeChatId" value="e1a95f8f-7831-4c3e-ba7d-bc949f94af4b" />
     <option name="chatMetadata" value="{&quot;e1a95f8f-7831-4c3e-ba7d-bc949f94af4b&quot;:{&quot;id&quot;:&quot;e1a95f8f-7831-4c3e-ba7d-bc949f94af4b&quot;,&quot;title&quot;:&quot;Write some detailed unit tests for our project ...&quot;,&quot;createdAt&quot;:1758942702303,&quot;updatedAt&quot;:1758942703244,&quot;isUnitTestsAgent&quot;:true,&quot;messageCount&quot;:2,&quot;lastMessagePreview&quot;:&quot;&quot;},&quot;744c3e10-33d2-414e-91ed-0472ce689be3&quot;:{&quot;id&quot;:&quot;744c3e10-33d2-414e-91ed-0472ce689be3&quot;,&quot;title&quot;:&quot;Postgres Model Definitions Update&quot;,&quot;createdAt&quot;:1757523010531,&quot;updatedAt&quot;:1757523030127,&quot;isAgent&quot;:true,&quot;isNameGenerated&quot;:true,&quot;messageCount&quot;:3,&quot;lastMessagePreview&quot;:&quot;**Modifying model structure**\n\nI need to adjust user.model.js to align with the new schema and creat...&quot;}}" />
   </component>
 </project>
\ No newline at end of file
index d6b5cfa3e7a5aebad0f5c049c5262e8a9f834650..2d619ac8c00ed49be38160da2ce90c488346f8f4 100755 (executable)
@@ -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 ) );
index 2c1a165eb0480208c3ed01d24c16391e77517d01..f5bd9525180b68b7094ffb51c02c89f862adfb97 100755 (executable)
@@ -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<File, 'id'|'created_at'|'is_deleted'|'deleted_by_id'|'deleted_at'> & {specific_users?: number[]}} file_data - File data
+   * @param {Omit<File, 'id'|'created_at'|'is_deleted'|'deleted_by_id'|'deleted_at'> & {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<File>} 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 );
index 635ff5bba03a319bb798a3d8647eccc3563bf10b..7ce883e348f74de14854cd1034e023ac2f3d04e2 100755 (executable)
@@ -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<Object>} 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;
index 04ca92fa025bf4af0b072d996810f9a98f6c9d2a..00c0f18b39002d659f85721fcb2d3a9316714950 100755 (executable)
@@ -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;
     } );
   }