testapp: add functionality to delete all recordings and enhance UI for action buttons

This commit is contained in:
Carlos Santos 2025-06-12 11:11:36 +02:00
parent 209b4023fc
commit 21fbeec258
6 changed files with 125 additions and 28 deletions

View File

@ -27,6 +27,10 @@ html {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.action-buttons-container {
display: inline-flex;
gap: 2px;
}
.rooms-container { .rooms-container {
flex: 2; flex: 2;
background: white; background: white;

View File

@ -21,17 +21,31 @@
</div> </div>
<div class="rooms-list"> <div class="rooms-list">
<div class="mb-3 text-center"> <div class="action-buttons-container mb-3 text-center">
<form action="/delete-all-rooms" method="post"> <div class="mb-3 text-center">
<button <form action="/delete-all-rooms" method="post">
type="submit" <button
class="btn btn-danger btn-sm" type="submit"
id="delete-all-rooms" class="btn btn-danger btn-sm"
> id="delete-all-rooms"
Delete All Rooms >
</button> Delete All Rooms
</form> </button>
</form>
</div>
<div class="mb-3 text-center">
<form action="/delete-all-recordings" method="post">
<button
type="submit"
class="btn btn-danger btn-sm"
id="delete-all-rooms"
>
Delete All Recordings
</button>
</form>
</div>
</div> </div>
{{#rooms.length}} {{#rooms.length}}
<ul class="list-group"> <ul class="list-group">
{{#rooms}} {{#rooms}}

View File

@ -5,6 +5,10 @@ import {
deleteRoom, deleteRoom,
deleteAllRooms, deleteAllRooms,
} from '../services/roomService'; } from '../services/roomService';
import {
deleteAllRecordings,
getAllRecordings,
} from '../services/recordingService';
export const getHome = async (req: Request, res: Response) => { export const getHome = async (req: Request, res: Response) => {
try { try {
@ -61,6 +65,7 @@ export const deleteAllRoomsCtrl = async (_req: Request, res: Response) => {
return; return;
} }
const roomIds = allRooms.rooms.map((room) => room.roomId); const roomIds = allRooms.rooms.map((room) => room.roomId);
console.log(`Deleting ${roomIds.length} rooms`, roomIds);
await deleteAllRooms(roomIds); await deleteAllRooms(roomIds);
res.render('index', { rooms: [] }); res.render('index', { rooms: [] });
} catch (error) { } catch (error) {
@ -70,26 +75,50 @@ export const deleteAllRoomsCtrl = async (_req: Request, res: Response) => {
} }
}; };
export const deleteAllRecordingsCtrl = async (_req: Request, res: Response) => {
try {
const [{ recordings }, { rooms }] = await Promise.all([
getAllRecordings(),
getAllRooms(),
]);
if (recordings.length === 0) {
console.log('No recordings to delete');
res.render('index', { rooms });
return;
}
const recordingIds = recordings.map((recording) => recording.recordingId);
await deleteAllRecordings(recordingIds);
console.log(`Deleted ${recordingIds.length} recordings`);
res.render('index', { rooms });
} catch (error) {
console.error('Error deleting all recordings:', error);
res.status(500).send('Internal Server Error ' + JSON.stringify(error));
return;
}
};
/** /**
* Converts flat form data to nested MeetRoomPreferences object * Converts flat form data to nested MeetRoomPreferences object
*/ */
const processFormPreferences = (body: any): any => { const processFormPreferences = (body: any): any => {
const preferences = { const preferences = {
chatPreferences: { chatPreferences: {
enabled: body['preferences.chatPreferences.enabled'] === 'on' enabled: body['preferences.chatPreferences.enabled'] === 'on',
}, },
recordingPreferences: { recordingPreferences: {
enabled: body['preferences.recordingPreferences.enabled'] === 'on', enabled: body['preferences.recordingPreferences.enabled'] === 'on',
// Only include allowAccessTo if recording is enabled // Only include allowAccessTo if recording is enabled
...(body['preferences.recordingPreferences.enabled'] === 'on' && { ...(body['preferences.recordingPreferences.enabled'] === 'on' && {
allowAccessTo: body['preferences.recordingPreferences.allowAccessTo'] || 'admin-moderator-publisher' allowAccessTo:
}) body['preferences.recordingPreferences.allowAccessTo'] ||
}, 'admin-moderator-publisher',
virtualBackgroundPreferences: { }),
enabled: body['preferences.virtualBackgroundPreferences.enabled'] === 'on' },
} virtualBackgroundPreferences: {
}; enabled:
body['preferences.virtualBackgroundPreferences.enabled'] === 'on',
},
};
return preferences; return preferences;
} };

View File

@ -7,6 +7,7 @@ import {
postCreateRoom, postCreateRoom,
deleteRoomCtrl, deleteRoomCtrl,
deleteAllRoomsCtrl, deleteAllRoomsCtrl,
deleteAllRecordingsCtrl,
} from './controllers/homeController'; } from './controllers/homeController';
import { handleWebhook, joinRoom } from './controllers/roomController'; import { handleWebhook, joinRoom } from './controllers/roomController';
import { configService } from './services/configService'; import { configService } from './services/configService';
@ -34,6 +35,7 @@ app.get('/room', joinRoom);
app.post('/room', postCreateRoom); app.post('/room', postCreateRoom);
app.post('/room/delete', deleteRoomCtrl); app.post('/room/delete', deleteRoomCtrl);
app.post('/delete-all-rooms', deleteAllRoomsCtrl); app.post('/delete-all-rooms', deleteAllRoomsCtrl);
app.post('/delete-all-recordings', deleteAllRecordingsCtrl);
app.post('/join-room', joinRoom); app.post('/join-room', joinRoom);
app.post('/webhook', (req, res) => { app.post('/webhook', (req, res) => {
handleWebhook(req, res, io); handleWebhook(req, res, io);

View File

@ -0,0 +1,41 @@
import { del, get } from '../utils/http';
import { MeetRecordingInfo } from '../../../typings/src/recording.model';
import { configService } from './configService';
export const getAllRecordings = async (): Promise<{
pagination: { isTruncated: boolean; nextPageToken?: string };
recordings: MeetRecordingInfo[];
}> => {
const url = `${configService.meetApiUrl}/recordings`;
let { pagination, recordings } = await get<{
pagination: any;
recordings: MeetRecordingInfo[];
}>(url, {
headers: { 'x-api-key': configService.apiKey },
});
while (pagination.isTruncated) {
const nextPageUrl = `${url}?nextPageToken=${pagination.nextPageToken}`;
const nextPageResult = await get<{
pagination: any;
recordings: MeetRecordingInfo[];
}>(nextPageUrl, {
headers: { 'x-api-key': configService.apiKey },
});
recordings.push(...nextPageResult.recordings);
pagination = nextPageResult.pagination;
}
return { pagination, recordings };
};
export const deleteAllRecordings = async (
recordingIds: string[]
): Promise<void> => {
const url = `${
configService.meetApiUrl
}/recordings?recordingIds=${recordingIds.join(',')}`;
await del<void>(url, {
headers: { 'x-api-key': configService.apiKey },
});
};

View File

@ -31,7 +31,14 @@ async function request<T>(
const text = await response.text(); const text = await response.text();
throw new Error(`HTTP ${response.status}: ${text}`); throw new Error(`HTTP ${response.status}: ${text}`);
} }
return response.json() as Promise<T>;
// Handle empty responses (e.g., for DELETE requests)
const text = await response.text();
if (!text) {
return {} as T;
}
return JSON.parse(text) as T;
} }
export function get<T>( export function get<T>(