openvibers/openvibe.chat Commits
Chat: accept attachments in room messages (#473)
Changes
@@@ -40,10 +40,19 @@ function cleanOne(file, index, reader = null) {/** The files a message may carry (at most MAX_FILES), or [] when it has none. */function cleanFiles(files, reader = null) {if (!Array.isArray(files) || !files.length) return [];-- if (files.length > MAX_FILES) throw new RoomError(413, 'rooms.files_too_many', `At most ${MAX_FILES} files can go with a message`);++ if (files.length > MAX_FILES) {++ const { RoomError } = require('./rooms');++ throw new RoomError(413, 'rooms.files_too_many', `At most ${MAX_FILES} files can go with a message`);++ }return files.map((f, i) => cleanOne(f, i, reader));}++/** The message API calls these attachments; the AI pipeline keeps its historical `files` name. */++function attachmentsFromBody(body) {++ if (!body || typeof body !== 'object') return [];++ return Array.isArray(body.attachments) ? body.attachments : (Array.isArray(body.files) ? body.files : []);++}++/** The files stored on a message row (jsonb comes back as an array; a text column is tolerated too). */function filesOf(message) {if (!message) return [];@@@ -52,4 +61,4 @@ function filesOf(message) {return Array.isArray(files) ? files : [];}--module.exports = { cleanFiles, filesOf, MAX_FILES };++module.exports = { cleanFiles, filesOf, attachmentsFromBody, MAX_FILES };@@@ -211,7 +211,7 @@ router.post('/:slug/messages', requireAuth, postLimit, async (req, res) => {try {const message = await rooms.post(room, req.user, req.body && req.body.message, {timezone: req.body && req.body.timezone,-- files: req.body && req.body.files,++ files: require('./files').attachmentsFromBody(req.body),reply_to_id: req.body && req.body.reply_to_id,});chatServer().broadcastToRoom(room.id, { type: 'room_message', room: room.slug, message });nnew file mode 100644@@@ -0,0 +1,14 @@++'use strict';++const assert = require('node:assert/strict');++const { suite } = require('./helpers');++const t = suite('room attachments');++const files = require('../server/rooms/files');++++t('room message attachments accept the public field name and enforce the ten-file limit', () => {++ assert.deepEqual(files.attachmentsFromBody({ attachments: [{ id: 'a' }] }), [{ id: 'a' }]);++ assert.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);++});++++t.run();
@@ -40,10 +40,19 @@ function cleanOne(file, index, reader = null) {
@@ -40,10 +40,19 @@ function cleanOne(file, index, reader = null) {
/** The files a message may carry (at most MAX_FILES), or [] when it has none. */
/** The files a message may carry (at most MAX_FILES), or [] when it has none. */
function cleanFiles(files, reader = null) {
function cleanFiles(files, reader = null) {
if (!Array.isArray(files) || !files.length) return [];
if (!Array.isArray(files) || !files.length) return [];
- if (files.length > MAX_FILES) throw new RoomError(413, 'rooms.files_too_many', `At most ${MAX_FILES} files can go with a message`);
+ if (files.length > MAX_FILES) {
+ const { RoomError } = require('./rooms');
+ throw new RoomError(413, 'rooms.files_too_many', `At most ${MAX_FILES} files can go with a message`);
+ }
return files.map((f, i) => cleanOne(f, i, reader));
return files.map((f, i) => cleanOne(f, i, reader));
}
}
+/** The message API calls these attachments; the AI pipeline keeps its historical `files` name. */
+function attachmentsFromBody(body) {
+ if (!body || typeof body !== 'object') return [];
+ return Array.isArray(body.attachments) ? body.attachments : (Array.isArray(body.files) ? body.files : []);
+}
+
/** The files stored on a message row (jsonb comes back as an array; a text column is tolerated too). */
/** The files stored on a message row (jsonb comes back as an array; a text column is tolerated too). */
function filesOf(message) {
function filesOf(message) {
if (!message) return [];
if (!message) return [];
@@ -52,4 +61,4 @@ function filesOf(message) {
@@ -52,4 +61,4 @@ function filesOf(message) {
return Array.isArray(files) ? files : [];
return Array.isArray(files) ? files : [];
}
}
-module.exports = { cleanFiles, filesOf, MAX_FILES };
+module.exports = { cleanFiles, filesOf, attachmentsFromBody, MAX_FILES };
@@ -211,7 +211,7 @@ router.post('/:slug/messages', requireAuth, postLimit, async (req, res) => {
@@ -211,7 +211,7 @@ router.post('/:slug/messages', requireAuth, postLimit, async (req, res) => {
try {
try {
const message = await rooms.post(room, req.user, req.body && req.body.message, {
const message = await rooms.post(room, req.user, req.body && req.body.message, {
timezone: req.body && req.body.timezone,
timezone: req.body && req.body.timezone,
- files: req.body && req.body.files,
+ files: require('./files').attachmentsFromBody(req.body),
reply_to_id: req.body && req.body.reply_to_id,
reply_to_id: req.body && req.body.reply_to_id,
});
});
chatServer().broadcastToRoom(room.id, { type: 'room_message', room: room.slug, message });
chatServer().broadcastToRoom(room.id, { type: 'room_message', room: room.slug, message });
new file mode 100644
new file mode 100644
@@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
+'use strict';
+const assert = require('node:assert/strict');
+const { suite } = require('./helpers');
+const t = suite('room attachments');
+const files = require('../server/rooms/files');
+
+t('room message attachments accept the public field name and enforce the ten-file limit', () => {
+ assert.deepEqual(files.attachmentsFromBody({ attachments: [{ id: 'a' }] }), [{ id: 'a' }]);
+ assert.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);
+});
+
+t.run();