Initial
This commit is contained in:
331
server/forms/schemas.js
Normal file
331
server/forms/schemas.js
Normal file
@@ -0,0 +1,331 @@
|
||||
import { z } from 'zod';
|
||||
import { isAllowedAuthorityHost, isAllowedGraphHost } from '../data/graphHosts.js';
|
||||
|
||||
export const loginSchema = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(1)
|
||||
});
|
||||
|
||||
export const profileSchema = z.object({
|
||||
email: z.string().email().optional(),
|
||||
displayName: z.string().min(1).max(120),
|
||||
jobTitle: z.string().max(120).optional().default(''),
|
||||
phone: z.string().max(60).optional().default(''),
|
||||
timezone: z.string().max(80).optional().default(''),
|
||||
avatarUrl: z.string().url().or(z.literal('')).optional().default('')
|
||||
});
|
||||
|
||||
export const themeSchema = z.object({
|
||||
theme: z.string().min(1).max(48).regex(/^[a-z0-9_-]+$/)
|
||||
});
|
||||
|
||||
export const passwordChangeSchema = z.object({
|
||||
currentPassword: z.string().optional().default(''),
|
||||
newPassword: z.string().optional().default('')
|
||||
});
|
||||
|
||||
export const visibilitySchema = z.enum(['personal', 'shared', 'group']);
|
||||
|
||||
export const userSchema = z.object({
|
||||
email: z.string().email(),
|
||||
displayName: z.string().min(1),
|
||||
password: z.string().min(8).optional(),
|
||||
role: z.enum(['admin', 'user']).default('user'),
|
||||
groupIds: z.array(z.string()).default([])
|
||||
});
|
||||
|
||||
export const groupSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
userIds: z.array(z.string()).default([])
|
||||
});
|
||||
|
||||
export const credentialSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
kind: z.enum(['username_password', 'api_key']),
|
||||
username: z.string().optional(),
|
||||
secret: z.string().min(1),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const hostSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
fqdn: z.string().optional(),
|
||||
address: z.string().min(1),
|
||||
osFamily: z.string().default('windows'),
|
||||
transport: z.enum(['winrm', 'ssh', 'local', 'api']).default('winrm'),
|
||||
port: z.number().int().nullable().optional(),
|
||||
credentialId: z.string().nullable().optional(),
|
||||
tags: z.array(z.string()).default([]),
|
||||
notes: z.string().optional(),
|
||||
visibility: visibilitySchema.default('shared'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const folderSchema = z.object({
|
||||
parentId: z.string().nullable().optional(),
|
||||
name: z.string().min(1),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const assetFolderSchema = z.object({
|
||||
parentId: z.string().nullable().optional(),
|
||||
name: z.string().min(1).max(180),
|
||||
description: z.string().max(600).optional().default(''),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const assetUploadSchema = z.object({
|
||||
folderId: z.string().nullable().optional(),
|
||||
name: z.string().min(1).max(220),
|
||||
originalName: z.string().min(1).max(260).optional(),
|
||||
description: z.string().max(1200).optional().default(''),
|
||||
mimeType: z.string().max(160).optional(),
|
||||
packagePath: z.string().max(500).optional().default(''),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const assetUpdateSchema = z.object({
|
||||
folderId: z.string().nullable().optional(),
|
||||
name: z.string().min(1).max(220).optional(),
|
||||
description: z.string().max(1200).optional(),
|
||||
packagePath: z.string().max(500).optional(),
|
||||
visibility: visibilitySchema.optional(),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const assetLinkSchema = z.object({
|
||||
targetType: z.enum(['script', 'runplan', 'psadt_profile', 'intune_deployment']).default('script'),
|
||||
targetId: z.string().min(1),
|
||||
linkRole: z.enum(['reference', 'package-file', 'installer', 'support-file', 'icon', 'detection-script']).default('reference'),
|
||||
packagePath: z.string().max(500).optional().default('')
|
||||
});
|
||||
|
||||
export const variableNameSchema = z.string().min(1).max(80).regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
|
||||
|
||||
export const customVariableSchema = z.object({
|
||||
name: variableNameSchema,
|
||||
description: z.string().max(500).optional().default(''),
|
||||
category: z.string().min(1).max(80).optional().default('Custom'),
|
||||
value: z.string().optional().default(''),
|
||||
valueType: z.enum(['string', 'number', 'boolean', 'json']).default('string'),
|
||||
sensitive: z.boolean().or(z.number()).optional().default(false),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const psadtProfileSchema = z.object({
|
||||
name: z.string().min(1).max(160),
|
||||
appVendor: z.string().max(120).optional().default(''),
|
||||
appName: z.string().max(160).optional().default(''),
|
||||
appVersion: z.string().max(80).optional().default(''),
|
||||
appArch: z.enum(['x64', 'x86', 'neutral']).optional().default('x64'),
|
||||
appLang: z.string().max(40).optional().default('EN'),
|
||||
appRevision: z.string().max(40).optional().default('01'),
|
||||
toolkitVersion: z.enum(['v4', 'v3-compatible']).optional().default('v4'),
|
||||
templateVersion: z.enum(['v4', 'v3']).optional().default('v4'),
|
||||
deploymentType: z.enum(['Install', 'Uninstall', 'Repair']).optional().default('Install'),
|
||||
deployMode: z.enum(['Auto', 'Interactive', 'Silent', 'NonInteractive']).optional().default('Auto'),
|
||||
requireAdmin: z.boolean().or(z.number()).optional().default(true),
|
||||
zeroConfig: z.boolean().or(z.number()).optional().default(false),
|
||||
suppressReboot: z.boolean().or(z.number()).optional().default(false),
|
||||
closeProcesses: z.array(z.object({
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional().default('')
|
||||
})).optional().default([]),
|
||||
installTasks: z.array(z.object({
|
||||
type: z.enum(['exe', 'msi', 'msp', 'script']).default('exe'),
|
||||
phase: z.enum(['Pre-Install', 'Install', 'Post-Install', 'Pre-Uninstall', 'Uninstall', 'Post-Uninstall', 'Pre-Repair', 'Repair', 'Post-Repair']).default('Install'),
|
||||
filePath: z.string().min(1),
|
||||
arguments: z.string().optional().default(''),
|
||||
secureArguments: z.boolean().or(z.number()).optional().default(false)
|
||||
})).optional().default([]),
|
||||
uiPlan: z.record(z.string(), z.any()).optional().default({}),
|
||||
configPlan: z.record(z.string(), z.any()).optional().default({}),
|
||||
admxPlan: z.record(z.string(), z.any()).optional().default({}),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const psadtValidationSchema = z.object({
|
||||
content: z.string().optional().default(''),
|
||||
scriptId: z.string().optional(),
|
||||
profileId: z.string().optional(),
|
||||
platform: z.enum(['intune', 'configmgr', 'gpo', 'manual']).optional().default('intune')
|
||||
});
|
||||
|
||||
export const psadtMigrationSchema = z.object({
|
||||
content: z.string().optional().default(''),
|
||||
scriptId: z.string().optional(),
|
||||
targetVersion: z.string().optional().default('4.2.0'),
|
||||
createScript: z.boolean().or(z.number()).optional().default(false),
|
||||
nameSuffix: z.string().max(80).optional().default('PSADT 4.2 migrated')
|
||||
});
|
||||
|
||||
export const intuneDeploymentSchema = z.object({
|
||||
name: z.string().min(1).max(160),
|
||||
profileId: z.string().nullable().optional(),
|
||||
scriptId: z.string().nullable().optional(),
|
||||
applicationId: z.string().nullable().optional(),
|
||||
sourceFolder: z.string().max(500).optional().default(''),
|
||||
intunewinFile: z.string().max(500).optional().default(''),
|
||||
appType: z.string().min(1).max(80).optional().default('Windows app (Win32)'),
|
||||
commandStyle: z.enum(['v4', 'legacy-v3']).optional().default('v4'),
|
||||
installBehavior: z.enum(['system', 'user']).optional().default('system'),
|
||||
restartBehavior: z.enum(['return-code', 'suppress', 'intune']).optional().default('return-code'),
|
||||
uiMode: z.enum(['native-v4', 'serviceui-legacy', 'silent']).optional().default('native-v4'),
|
||||
requirements: z.object({
|
||||
architecture: z.enum(['x64', 'x86', 'both']).optional().default('x64'),
|
||||
minOs: z.string().max(80).optional().default('Windows 10 22H2'),
|
||||
diskSpaceMb: z.number().or(z.string()).optional().default(0),
|
||||
runAs32Bit: z.boolean().or(z.number()).optional().default(false)
|
||||
}).optional().default({}),
|
||||
installCommand: z.string().max(500).optional().default(''),
|
||||
uninstallCommand: z.string().max(500).optional().default(''),
|
||||
detectionType: z.enum(['msi-product-code', 'file-version', 'registry', 'custom-script']).optional().default('custom-script'),
|
||||
detectionRule: z.string().max(3000).optional().default(''),
|
||||
returnCodes: z.array(z.object({
|
||||
code: z.string().or(z.number()),
|
||||
type: z.string().min(1).max(40),
|
||||
meaning: z.string().max(240).optional().default('')
|
||||
})).optional().default([]),
|
||||
assignments: z.array(z.object({
|
||||
ring: z.string().min(1).max(80),
|
||||
intent: z.enum(['available', 'required', 'uninstall', 'exclude']).optional().default('required'),
|
||||
groupName: z.string().max(160).optional().default(''),
|
||||
groupId: z.string().max(120).optional().default(''),
|
||||
groupMode: z.enum(['group', 'allDevices', 'allUsers']).optional().default('group'),
|
||||
filterId: z.string().max(120).optional().default(''),
|
||||
filterType: z.enum(['none', 'include', 'exclude']).optional().default('none'),
|
||||
autoUpdate: z.boolean().or(z.number()).optional().default(false),
|
||||
notes: z.string().max(240).optional().default('')
|
||||
})).optional().default([]),
|
||||
relationships: z.array(z.object({
|
||||
kind: z.enum(['supersedence', 'dependency']).default('supersedence'),
|
||||
targetAppId: z.string().min(1).max(160),
|
||||
targetName: z.string().max(200).optional().default(''),
|
||||
mode: z.string().max(40).optional().default('')
|
||||
})).optional().default([]),
|
||||
status: z.enum(['draft', 'ready', 'published', 'retired']).optional().default('draft'),
|
||||
autoPromoteAfterHours: z.number().int().min(0).or(z.string()).optional().default(0),
|
||||
autoPromoteRing: z.string().max(80).optional().default(''),
|
||||
autoPromoteIntent: z.enum(['available', 'required', 'uninstall', 'exclude']).optional().default('required'),
|
||||
graphAppId: z.string().max(160).optional().default(''),
|
||||
graphConnectionId: z.string().nullable().optional(),
|
||||
notes: z.string().max(1000).optional().default(''),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const applicationSchema = z.object({
|
||||
name: z.string().min(1).max(160),
|
||||
vendor: z.string().max(120).optional().default(''),
|
||||
publisher: z.string().max(160).optional().default(''),
|
||||
currentVersion: z.string().max(80).optional().default(''),
|
||||
latestKnownVersion: z.string().max(80).optional().default(''),
|
||||
versionSource: z.enum(['manual', 'url', 'winget']).optional().default('manual'),
|
||||
versionSourceRef: z.string().max(500).optional().default(''),
|
||||
graphConnectionId: z.string().nullable().optional(),
|
||||
currentGraphAppId: z.string().max(160).optional().default(''),
|
||||
autoCheck: z.boolean().or(z.number()).optional().default(false),
|
||||
notes: z.string().max(1000).optional().default(''),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const applicationImportSchema = z.object({
|
||||
connectionId: z.string().min(1),
|
||||
graphAppId: z.string().min(1).max(160),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const graphConnectionSchema = z.object({
|
||||
name: z.string().min(1).max(160),
|
||||
tenantId: z.string().min(1).max(120),
|
||||
clientId: z.string().min(1).max(120),
|
||||
clientSecret: z.string().optional().default(''),
|
||||
cloud: z.enum(['global', 'gcc', 'gcchigh', 'dod', 'china', 'custom']).optional().default('global'),
|
||||
graphBaseUrl: z.string().url().refine(isAllowedGraphHost, {
|
||||
message: 'graphBaseUrl must be a recognized Microsoft Graph host'
|
||||
}).optional().default('https://graph.microsoft.com'),
|
||||
authorityHost: z.string().url().refine(isAllowedAuthorityHost, {
|
||||
message: 'authorityHost must be a recognized Microsoft login host'
|
||||
}).optional().default('https://login.microsoftonline.com'),
|
||||
defaultApiVersion: z.enum(['v1.0', 'beta']).optional().default('v1.0'),
|
||||
enabled: z.boolean().or(z.number()).optional().default(true),
|
||||
allowWrite: z.boolean().or(z.number()).optional().default(false),
|
||||
requireApproval: z.boolean().or(z.number()).optional().default(false),
|
||||
publisherIds: z.array(z.string()).optional().default([]),
|
||||
approverIds: z.array(z.string()).optional().default([]),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const graphDeploymentLinkSchema = z.object({
|
||||
connectionId: z.string().optional().default(''),
|
||||
graphAppId: z.string().min(1).max(160)
|
||||
});
|
||||
|
||||
export const graphDeploymentSyncSchema = z.object({
|
||||
connectionId: z.string().optional().default(''),
|
||||
graphAppId: z.string().optional().default(''),
|
||||
mode: z.enum(['reports', 'legacy-status', 'both']).optional().default('reports')
|
||||
});
|
||||
|
||||
const scriptPayloadSchema = z.object({
|
||||
folderId: z.string().nullable().optional(),
|
||||
folder_id: z.string().nullable().optional(),
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional().default(''),
|
||||
content: z.string().default(''),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional(),
|
||||
group_id: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export const scriptTestRunSchema = z.object({
|
||||
hostId: z.string().min(1),
|
||||
credentialId: z.string().min(1)
|
||||
});
|
||||
|
||||
const runPlanPayloadSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional().default(''),
|
||||
scriptId: z.string().optional(),
|
||||
script_id: z.string().optional(),
|
||||
visibility: visibilitySchema.default('personal'),
|
||||
groupId: z.string().nullable().optional(),
|
||||
group_id: z.string().nullable().optional(),
|
||||
parallel: z.boolean().or(z.number()).default(true),
|
||||
hostIds: z.array(z.string()).optional()
|
||||
});
|
||||
|
||||
export function normalizeScriptPayload(body) {
|
||||
const parsed = scriptPayloadSchema.parse(body);
|
||||
return {
|
||||
folderId: parsed.folderId ?? parsed.folder_id ?? null,
|
||||
name: parsed.name,
|
||||
description: parsed.description || '',
|
||||
content: parsed.content || '',
|
||||
visibility: parsed.visibility,
|
||||
groupId: parsed.visibility === 'group' ? parsed.groupId ?? parsed.group_id ?? null : null
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeRunPlanPayload(body) {
|
||||
const parsed = runPlanPayloadSchema.parse(body);
|
||||
return {
|
||||
name: parsed.name,
|
||||
description: parsed.description || '',
|
||||
scriptId: parsed.scriptId || parsed.script_id,
|
||||
visibility: parsed.visibility,
|
||||
groupId: parsed.visibility === 'group' ? parsed.groupId ?? parsed.group_id ?? null : null,
|
||||
parallel: Boolean(parsed.parallel),
|
||||
hostIds: parsed.hostIds || []
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user