39948-vm/backend/src/validators/request-schemas.js

358 lines
10 KiB
JavaScript

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,
};