openvibers/openvibe.chat Commits
Chat: expose typed room attachment references (#477)
Changes
@@@ -61,4 +61,18 @@ function filesOf(message) {return Array.isArray(files) ? files : [];}--module.exports = { cleanFiles, filesOf, attachmentsFromBody, MAX_FILES };++/** Public message attachment refs, projected from owner-validated Media ids. */++function attachmentRefs(files) {++ const origin = require('../config').media.publicOrigin;++ return (Array.isArray(files) ? files : []).map((file) => {++ const type = String(file.media_type || 'application/octet-stream');++ const kind = type.startsWith('image/') ? 'picture' : type.startsWith('video/') ? 'video' : type.startsWith('audio/') ? 'audio' : 'file';++ return {++ kind, service: 'media', id: file.id,++ url: `${origin}/o/${encodeURIComponent(file.id)}`,++ name: file.name || 'file', size: Number(file.size) || 0, type,++ };++ });++}++++module.exports = { cleanFiles, filesOf, attachmentRefs, attachmentsFromBody, MAX_FILES };@@@ -362,7 +362,10 @@ const MSG_SELECT = `SELECT m.id, m.room_id, m.user_id, m.message, m.card, m.fileLEFT JOIN room_ai_members a ON a.id = m.ai_member_id`;async function messageById(id) {const row = await db.get(`${MSG_SELECT} WHERE m.id = ?`, [id]) || null;-- if (row) await linkCards.hydrate([row], 'room_messages');++ if (row) {++ row.attachments = messageFiles.attachmentRefs(messageFiles.filesOf(row));++ await linkCards.hydrate([row], 'room_messages');++ }return row;}@@@ -374,6 +377,7 @@ async function history(room, { before = null, after = null, limit = 60 } = {}) {: before != null? await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.id < ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, Number(before) || 0, lim]): await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, lim]);++ for (const row of rows) row.attachments = messageFiles.attachmentRefs(messageFiles.filesOf(row));await linkCards.hydrate(rows, 'room_messages');return after != null ? rows : rows.reverse();}@@@ -9,6 +9,10 @@ t('room message attachments accept the public field name and enforce the ten-filassert.deepEqual(files.attachmentsFromBody({ files: [{ id: 'b' }] }), [{ id: 'b' }]);assert.deepEqual(files.attachmentsFromBody({ attachments: 'invalid', files: [{ id: 'b' }] }), [{ id: 'b' }]);assert.throws(() => files.cleanFiles(Array(11).fill({ id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV' })), (err) => err.code === 'rooms.files_too_many' && err.status === 413);++ assert.deepEqual(files.attachmentRefs([{ id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV', name: 'scan.pdf', size: 12, media_type: 'application/pdf' }]), [{++ kind: 'file', service: 'media', id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV', url: 'https://openvibe.media/o/med_01ARZ3NDEKTSV4RRFFQ69G5FAV',++ name: 'scan.pdf', size: 12, type: 'application/pdf',++ }]);});t.run();
@@ -61,4 +61,18 @@ function filesOf(message) {
@@ -61,4 +61,18 @@ function filesOf(message) {
return Array.isArray(files) ? files : [];
return Array.isArray(files) ? files : [];
}
}
-module.exports = { cleanFiles, filesOf, attachmentsFromBody, MAX_FILES };
+/** Public message attachment refs, projected from owner-validated Media ids. */
+function attachmentRefs(files) {
+ const origin = require('../config').media.publicOrigin;
+ return (Array.isArray(files) ? files : []).map((file) => {
+ const type = String(file.media_type || 'application/octet-stream');
+ const kind = type.startsWith('image/') ? 'picture' : type.startsWith('video/') ? 'video' : type.startsWith('audio/') ? 'audio' : 'file';
+ return {
+ kind, service: 'media', id: file.id,
+ url: `${origin}/o/${encodeURIComponent(file.id)}`,
+ name: file.name || 'file', size: Number(file.size) || 0, type,
+ };
+ });
+}
+
+module.exports = { cleanFiles, filesOf, attachmentRefs, attachmentsFromBody, MAX_FILES };
@@ -362,7 +362,10 @@ const MSG_SELECT = `SELECT m.id, m.room_id, m.user_id, m.message, m.card, m.file
@@ -362,7 +362,10 @@ const MSG_SELECT = `SELECT m.id, m.room_id, m.user_id, m.message, m.card, m.file
LEFT JOIN room_ai_members a ON a.id = m.ai_member_id`;
LEFT JOIN room_ai_members a ON a.id = m.ai_member_id`;
async function messageById(id) {
async function messageById(id) {
const row = await db.get(`${MSG_SELECT} WHERE m.id = ?`, [id]) || null;
const row = await db.get(`${MSG_SELECT} WHERE m.id = ?`, [id]) || null;
- if (row) await linkCards.hydrate([row], 'room_messages');
+ if (row) {
+ row.attachments = messageFiles.attachmentRefs(messageFiles.filesOf(row));
+ await linkCards.hydrate([row], 'room_messages');
+ }
return row;
return row;
}
}
@@ -374,6 +377,7 @@ async function history(room, { before = null, after = null, limit = 60 } = {}) {
@@ -374,6 +377,7 @@ async function history(room, { before = null, after = null, limit = 60 } = {}) {
: before != null
: before != null
? await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.id < ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, Number(before) || 0, lim])
? await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.id < ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, Number(before) || 0, lim])
: await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, lim]);
: await db.all(`${MSG_SELECT} WHERE m.room_id = ? AND m.is_deleted = 0 ORDER BY m.id DESC LIMIT ?`, [room.id, lim]);
+ for (const row of rows) row.attachments = messageFiles.attachmentRefs(messageFiles.filesOf(row));
await linkCards.hydrate(rows, 'room_messages');
await linkCards.hydrate(rows, 'room_messages');
return after != null ? rows : rows.reverse();
return after != null ? rows : rows.reverse();
}
}
@@ -9,6 +9,10 @@ t('room message attachments accept the public field name and enforce the ten-fil
@@ -9,6 +9,10 @@ t('room message attachments accept the public field name and enforce the ten-fil
assert.deepEqual(files.attachmentsFromBody({ files: [{ id: 'b' }] }), [{ id: 'b' }]);
assert.deepEqual(files.attachmentsFromBody({ files: [{ id: 'b' }] }), [{ id: 'b' }]);
assert.deepEqual(files.attachmentsFromBody({ attachments: 'invalid', files: [{ id: 'b' }] }), [{ id: 'b' }]);
assert.deepEqual(files.attachmentsFromBody({ attachments: 'invalid', files: [{ id: 'b' }] }), [{ id: 'b' }]);
assert.throws(() => files.cleanFiles(Array(11).fill({ id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV' })), (err) => err.code === 'rooms.files_too_many' && err.status === 413);
assert.throws(() => files.cleanFiles(Array(11).fill({ id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV' })), (err) => err.code === 'rooms.files_too_many' && err.status === 413);
+ assert.deepEqual(files.attachmentRefs([{ id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV', name: 'scan.pdf', size: 12, media_type: 'application/pdf' }]), [{
+ kind: 'file', service: 'media', id: 'med_01ARZ3NDEKTSV4RRFFQ69G5FAV', url: 'https://openvibe.media/o/med_01ARZ3NDEKTSV4RRFFQ69G5FAV',
+ name: 'scan.pdf', size: 12, type: 'application/pdf',
+ }]);
});
});
t.run();
t.run();