backend: implement rooms appearance configuration endpoints and validation
This commit is contained in:
parent
bb5a86da2c
commit
5cdf4370bf
@ -1,12 +1,48 @@
|
||||
import { MeetAppearanceConfig } from '@typings-ce';
|
||||
import { Request, Response } from 'express';
|
||||
import { errorProFeature, rejectRequestFromMeetError } from '../../models/error.model.js';
|
||||
import { container } from '../../config/index.js';
|
||||
import {
|
||||
errorRoomsAppearanceConfigNotDefined,
|
||||
handleError,
|
||||
rejectRequestFromMeetError
|
||||
} from '../../models/error.model.js';
|
||||
import { LoggerService, MeetStorageService } from '../../services/index.js';
|
||||
|
||||
export const updateAppearanceConfig = async (_req: Request, res: Response) => {
|
||||
const error = errorProFeature('update appearance config');
|
||||
rejectRequestFromMeetError(res, error);
|
||||
export const updateRoomsAppearanceConfig = async (req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const storageService = container.get(MeetStorageService);
|
||||
|
||||
logger.verbose(`Updating rooms appearance config: ${JSON.stringify(req.body)}`);
|
||||
const appearanceConfig = req.body as { appearance: MeetAppearanceConfig };
|
||||
|
||||
try {
|
||||
const globalConfig = await storageService.getGlobalConfig();
|
||||
globalConfig.roomsConfig = appearanceConfig;
|
||||
await storageService.saveGlobalConfig(globalConfig);
|
||||
|
||||
return res.status(200).json({ message: 'Rooms appearance config updated successfully' });
|
||||
} catch (error) {
|
||||
handleError(res, error, 'updating rooms appearance config');
|
||||
}
|
||||
};
|
||||
|
||||
export const getAppearanceConfig = async (_req: Request, res: Response) => {
|
||||
const error = errorProFeature('get appearance config');
|
||||
rejectRequestFromMeetError(res, error);
|
||||
export const getRoomsAppearanceConfig = async (_req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const storageService = container.get(MeetStorageService);
|
||||
|
||||
logger.verbose(`Getting rooms appearance config`);
|
||||
|
||||
try {
|
||||
const globalConfig = await storageService.getGlobalConfig();
|
||||
const appearanceConfig = globalConfig.roomsConfig?.appearance;
|
||||
|
||||
if (!appearanceConfig) {
|
||||
const error = errorRoomsAppearanceConfigNotDefined();
|
||||
return rejectRequestFromMeetError(res, error);
|
||||
}
|
||||
|
||||
return res.status(200).json({ appearance: appearanceConfig });
|
||||
} catch (error) {
|
||||
handleError(res, error, 'getting rooms appearance config');
|
||||
}
|
||||
};
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { rejectUnprocessableRequest } from '../../models/error.model.js';
|
||||
import { AppearanceConfigSchema } from './room-validator.middleware.js';
|
||||
|
||||
const WebhookConfigSchema: z.ZodType<WebhookConfig> = z
|
||||
.object({
|
||||
@ -57,6 +58,10 @@ const SecurityConfigSchema: z.ZodType<SecurityConfig> = z.object({
|
||||
authentication: AuthenticationConfigSchema
|
||||
});
|
||||
|
||||
const RoomsAppearanceConfigSchema = z.object({
|
||||
appearance: AppearanceConfigSchema
|
||||
});
|
||||
|
||||
export const validateWebhookConfig = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = WebhookConfigSchema.safeParse(req.body);
|
||||
|
||||
@ -89,3 +94,14 @@ export const validateSecurityConfig = (req: Request, res: Response, next: NextFu
|
||||
req.body = data;
|
||||
next();
|
||||
};
|
||||
|
||||
export const validateRoomsAppearanceConfig = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = RoomsAppearanceConfigSchema.safeParse(req.body);
|
||||
|
||||
if (!success) {
|
||||
return rejectUnprocessableRequest(res, error);
|
||||
}
|
||||
|
||||
req.body = data;
|
||||
next();
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
MeetRoomFilters,
|
||||
MeetRoomOptions,
|
||||
MeetRoomStatus,
|
||||
MeetRoomThemeMode,
|
||||
MeetVirtualBackgroundConfig,
|
||||
ParticipantRole,
|
||||
RecordingPermissions
|
||||
@ -89,10 +90,28 @@ const VirtualBackgroundConfigSchema: z.ZodType<MeetVirtualBackgroundConfig> = z.
|
||||
enabled: z.boolean()
|
||||
});
|
||||
|
||||
const ThemeModeSchema: z.ZodType<MeetRoomThemeMode> = z.enum([MeetRoomThemeMode.LIGHT, MeetRoomThemeMode.DARK]);
|
||||
|
||||
const hexColorSchema = z.string().regex(/^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$/, 'Must be a valid hex color code');
|
||||
|
||||
const RoomThemeSchema = z.object({
|
||||
name: z.string().min(1, 'Theme name cannot be empty').max(50, 'Theme name cannot exceed 50 characters'),
|
||||
baseTheme: ThemeModeSchema,
|
||||
backgroundColor: hexColorSchema.optional(),
|
||||
primaryColor: hexColorSchema.optional(),
|
||||
secondaryColor: hexColorSchema.optional(),
|
||||
surfaceColor: hexColorSchema.optional()
|
||||
});
|
||||
|
||||
export const AppearanceConfigSchema = z.object({
|
||||
themes: z.array(RoomThemeSchema).length(1, 'There must be exactly one theme defined')
|
||||
});
|
||||
|
||||
const RoomConfigSchema: z.ZodType<MeetRoomConfig> = z.object({
|
||||
recording: RecordingConfigSchema,
|
||||
chat: ChatConfigSchema,
|
||||
virtualBackground: VirtualBackgroundConfigSchema
|
||||
// appearance: AppearanceConfigSchema,
|
||||
});
|
||||
|
||||
const RoomDeletionPolicyWithMeetingSchema: z.ZodType<MeetRoomDeletionPolicyWithMeeting> = z.enum([
|
||||
|
||||
@ -63,18 +63,6 @@ export const errorAzureNotAvailable = (error: any): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('ABS Error', `Azure Blob Storage is not available ${error}`, 503);
|
||||
};
|
||||
|
||||
export const errorInvalidWebhookUrl = (url: string, reason: string): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Webhook Error', `Webhook URL '${url}' is invalid: ${reason}`, 400);
|
||||
};
|
||||
|
||||
export const errorApiKeyNotConfiguredForWebhooks = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError(
|
||||
'Webhook Error',
|
||||
'There are no API keys configured yet. Please, create one to use webhooks.',
|
||||
400
|
||||
);
|
||||
};
|
||||
|
||||
// Auth errors
|
||||
|
||||
export const errorInvalidCredentials = (): OpenViduMeetError => {
|
||||
@ -259,6 +247,26 @@ export const errorParticipantIdentityNotProvided = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Participant Error', 'No participant identity provided', 400);
|
||||
};
|
||||
|
||||
// Global config errors
|
||||
|
||||
export const errorRoomsAppearanceConfigNotDefined = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Global Config Error', 'Rooms appearance config not defined', 404);
|
||||
};
|
||||
|
||||
// Webhook errors
|
||||
|
||||
export const errorInvalidWebhookUrl = (url: string, reason: string): OpenViduMeetError => {
|
||||
return new OpenViduMeetError('Webhook Error', `Webhook URL '${url}' is invalid: ${reason}`, 400);
|
||||
};
|
||||
|
||||
export const errorApiKeyNotConfiguredForWebhooks = (): OpenViduMeetError => {
|
||||
return new OpenViduMeetError(
|
||||
'Webhook Error',
|
||||
'There are no API keys configured yet. Please, create one to use webhooks.',
|
||||
400
|
||||
);
|
||||
};
|
||||
|
||||
// Handlers
|
||||
|
||||
export const handleError = (res: Response, error: OpenViduMeetError | unknown, operationDescription: string) => {
|
||||
|
||||
@ -6,6 +6,7 @@ import * as securityConfigCtrl from '../controllers/global-config/security-confi
|
||||
import * as webhookConfigCtrl from '../controllers/global-config/webhook-config.controller.js';
|
||||
import {
|
||||
tokenAndRoleValidator,
|
||||
validateRoomsAppearanceConfig,
|
||||
validateSecurityConfig,
|
||||
validateWebhookConfig,
|
||||
withAuth,
|
||||
@ -37,12 +38,13 @@ configRouter.get('/security', securityConfigCtrl.getSecurityConfig);
|
||||
|
||||
// Appearance config
|
||||
configRouter.put(
|
||||
'/appearance',
|
||||
'/rooms/appearance',
|
||||
withAuth(tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
appearanceConfigCtrl.updateAppearanceConfig
|
||||
validateRoomsAppearanceConfig,
|
||||
appearanceConfigCtrl.updateRoomsAppearanceConfig
|
||||
);
|
||||
configRouter.get(
|
||||
'/appearance',
|
||||
'/rooms/appearance',
|
||||
withAuth(tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
appearanceConfigCtrl.getAppearanceConfig
|
||||
appearanceConfigCtrl.getRoomsAppearanceConfig
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user