const Joi = require('joi'); const uuid = Joi.string().guid({ version: ['uuidv4'] }); const idParam = Joi.object({ id: uuid.required() }).required(); const emptyQuery = Joi.object().max(0); const entityId = Joi.alternatives().try( uuid, Joi.object({ id: uuid.optional(), value: uuid.optional(), }) .or('id', 'value') .unknown(true), ); const sortDirection = Joi.string().valid('ASC', 'DESC', 'asc', 'desc'); const listQuery = Joi.object({ limit: Joi.number().integer().min(1).max(1000), page: Joi.number().integer().min(1), field: Joi.string() .trim() .max(128) .pattern(/^[A-Za-z0-9_.$]+$/), sort: sortDirection, filetype: Joi.string().valid('csv'), }) .unknown(true) .required(); const countQuery = listQuery; const autocompleteQuery = Joi.object({ query: Joi.string().allow('').max(255), limit: Joi.number().integer().min(1).max(50), offset: Joi.number().integer().min(0), }) .unknown(false) .required(); const dataEnvelope = Joi.object({ data: Joi.object().required().unknown(true), }) .unknown(false) .required(); const updateDataEnvelope = Joi.object({ id: uuid.optional(), data: Joi.object({ id: uuid.optional(), }) .required() .unknown(true), }) .unknown(false) .required(); const deleteByIdsBody = Joi.object({ data: Joi.array().items(uuid.required()).min(1).required(), }) .unknown(false) .required(); const optionalSlug = Joi.string() .trim() .max(255) .pattern(/^[a-z0-9_-]+$/i); const fileUrl = Joi.string().trim().min(1).max(4096); const userData = Joi.object({ id: uuid.optional(), firstName: Joi.string().allow('', null).max(255), lastName: Joi.string().allow('', null).max(255), phoneNumber: Joi.string().allow('', null).max(255), email: Joi.string().trim().email().allow(null), disabled: Joi.boolean(), password: Joi.string().allow('', null).max(1024), emailVerified: Joi.boolean(), emailVerificationToken: Joi.string().allow('', null).max(1024), emailVerificationTokenExpiresAt: Joi.date().allow(null), passwordResetToken: Joi.string().allow('', null).max(1024), passwordResetTokenExpiresAt: Joi.date().allow(null), provider: Joi.string().allow('', null).max(255), importHash: Joi.string().allow('', null).max(255), app_role: entityId.allow(null), custom_permissions: Joi.array().items(entityId), allowed_private_production_project_ids: Joi.array().items(entityId), avatar: Joi.any(), }).unknown(true); const userCreateData = userData.keys({ email: Joi.string().trim().email().required(), }); const projectData = Joi.object({ id: uuid.optional(), name: Joi.string().trim().min(1).max(255), slug: optionalSlug, description: Joi.string().allow('', null), logo_url: Joi.string().allow('', null).max(4096), favicon_url: Joi.string().allow('', null).max(4096), og_image_url: Joi.string().allow('', null).max(4096), design_width: Joi.number().integer().min(1).max(20000).allow(null), design_height: Joi.number().integer().min(1).max(20000).allow(null), production_presentation_visibility: Joi.string().valid('public', 'private'), importHash: Joi.string().allow('', null).max(255), }).unknown(true); const projectCreateData = projectData.keys({ name: Joi.string().trim().min(1).max(255).required(), slug: optionalSlug.required(), }); const uiSchema = Joi.alternatives().try( Joi.object({ elements: Joi.array().default([]) }).unknown(true), Joi.string().allow('', null), Joi.allow(null), ); const tourPageData = Joi.object({ id: uuid.optional(), project: uuid.optional(), projectId: uuid.optional(), project_id: uuid.optional(), environment: Joi.string().valid('dev', 'stage', 'production'), source_key: Joi.string().allow('', null).max(4096), name: Joi.string().trim().min(1).max(255), slug: optionalSlug, sort_order: Joi.number().integer().min(0), background_image_url: Joi.string().allow('', null).max(4096), background_video_url: Joi.string().allow('', null).max(4096), background_embed_url: Joi.string().allow('', null).max(8192), background_audio_url: Joi.string().allow('', null).max(4096), background_audio_autoplay: Joi.boolean(), background_audio_loop: Joi.boolean(), background_audio_start_time: Joi.number().min(0).allow(null), background_audio_end_time: Joi.number().min(0).allow(null), background_loop: Joi.boolean(), background_video_autoplay: Joi.boolean(), background_video_loop: Joi.boolean(), background_video_muted: Joi.boolean(), background_video_start_time: Joi.number().min(0).allow(null), background_video_end_time: Joi.number().min(0).allow(null), design_width: Joi.number().integer().min(1).max(20000).allow(null), design_height: Joi.number().integer().min(1).max(20000).allow(null), requires_auth: Joi.boolean(), ui_schema_json: uiSchema, global_ui_controls_settings_json: Joi.alternatives().try( Joi.object().unknown(true), Joi.string().allow('', null), Joi.allow(null), ), importHash: Joi.string().allow('', null).max(255), }).unknown(true); const tourPageCreateData = tourPageData .keys({ name: Joi.string().trim().min(1).max(255).required(), slug: optionalSlug.required(), }) .or('project', 'projectId', 'project_id'); function envelopeForData(dataSchema, { requireData = true } = {}) { return Joi.object({ id: uuid.optional(), data: requireData ? dataSchema.required() : dataSchema, }) .unknown(false) .required(); } module.exports = { auth: { signinLocal: { body: Joi.object({ email: Joi.string().trim().email().required(), password: Joi.string().required(), }) .unknown(false) .required(), }, passwordReset: { body: Joi.object({ token: Joi.string().trim().required(), password: Joi.string().required(), }) .unknown(false) .required(), }, passwordUpdate: { body: Joi.object({ currentPassword: Joi.string().required(), newPassword: Joi.string().required(), }) .unknown(false) .required(), }, sendPasswordResetEmail: { body: Joi.object({ email: Joi.string().trim().email().required(), }) .unknown(false) .required(), }, profile: { body: Joi.object({ profile: Joi.object().required().unknown(true), }) .unknown(false) .required(), }, verifyEmail: { body: Joi.object({ token: Joi.string().trim().required(), }) .unknown(false) .required(), }, socialSignin: { query: Joi.object({ app: Joi.string().allow('').max(255), }) .unknown(false) .required(), }, }, crud: { create: { body: dataEnvelope }, update: { params: idParam, body: updateDataEnvelope }, remove: { params: idParam }, deleteByIds: { body: deleteByIdsBody }, list: { query: listQuery }, count: { query: countQuery }, autocomplete: { query: autocompleteQuery }, findOne: { params: idParam }, }, file: { presign: { body: Joi.object({ urls: Joi.array().items(fileUrl.required()).min(1).max(50).required(), }) .unknown(false) .required(), }, upload: { params: Joi.object({ table: Joi.string() .trim() .max(128) .pattern(/^[A-Za-z0-9_-]+$/) .required(), field: Joi.string() .trim() .max(128) .pattern(/^[A-Za-z0-9_-]+$/) .required(), }).required(), }, initUploadSession: { body: Joi.object({ folder: Joi.string().trim().min(1).max(512).required(), filename: Joi.string().trim().min(1).max(255).required(), totalChunks: Joi.number().integer().min(1).max(10000).required(), size: Joi.number().min(0).required(), contentType: Joi.string().trim().allow('').max(255), }) .unknown(false) .required(), }, session: { params: Joi.object({ sessionId: uuid.required(), }).required(), }, chunk: { params: Joi.object({ sessionId: uuid.required(), chunkIndex: Joi.number().integer().min(0).max(9999).required(), }).required(), }, }, publish: { publish: { body: Joi.object({ projectId: uuid.required(), title: Joi.string().allow('', null).max(255), description: Joi.string().allow('', null).max(2000), }) .unknown(false) .required(), }, saveToStage: { body: Joi.object({ projectId: uuid.required(), }) .unknown(false) .required(), }, }, projects: { create: { body: envelopeForData(projectCreateData) }, update: { params: idParam, body: envelopeForData(projectData) }, clone: { params: idParam }, }, tourPages: { create: { body: envelopeForData(tourPageCreateData) }, update: { params: idParam, body: envelopeForData(tourPageData) }, reorder: { body: Joi.object({ data: Joi.object({ project: uuid.optional(), projectId: uuid.optional(), environment: Joi.string().valid('dev').default('dev'), orderedPageIds: Joi.array().items(uuid.required()).min(1).required(), }) .or('project', 'projectId') .required() .unknown(false), }) .unknown(false) .required(), }, duplicate: { params: idParam, body: Joi.object({ data: Joi.object({ project: uuid.optional(), projectId: uuid.optional(), environment: Joi.string().valid('dev').default('dev'), name: Joi.string().trim().allow('').max(255), slug: optionalSlug, }) .unknown(false) .default({}), }) .unknown(false) .required(), }, reverseVideoStatus: { body: Joi.object({ storageKeys: Joi.array() .items(fileUrl.required()) .min(1) .max(200) .required(), }) .unknown(false) .required(), }, }, users: { create: { body: envelopeForData(userCreateData) }, update: { params: idParam, body: envelopeForData(userData) }, }, emptyQuery, };