<?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="{"e1a95f8f-7831-4c3e-ba7d-bc949f94af4b":{"id":"e1a95f8f-7831-4c3e-ba7d-bc949f94af4b","title":"Write some detailed unit tests for our project ...","createdAt":1758942702303,"updatedAt":1758942703244,"isUnitTestsAgent":true,"messageCount":2,"lastMessagePreview":""},"744c3e10-33d2-414e-91ed-0472ce689be3":{"id":"744c3e10-33d2-414e-91ed-0472ce689be3","title":"Postgres Model Definitions Update","createdAt":1757523010531,"updatedAt":1757523030127,"isAgent":true,"isNameGenerated":true,"messageCount":3,"lastMessagePreview":"**Modifying model structure**\n\nI need to adjust user.model.js to align with the new schema and creat..."}}" />
</component>
</project>
\ No newline at end of file
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 ) );
/** @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,
/**
* 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 );
/**
* 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;
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
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;
} );
}