203 lines
6.9 KiB
TypeScript
203 lines
6.9 KiB
TypeScript
|
|
import { z } from 'zod'
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Base schemas for database entities
|
||
|
|
*/
|
||
|
|
|
||
|
|
// User schemas
|
||
|
|
export const UserRoleSchema = z.enum(['admin', 'user', 'artist', 'producer', 'moderator'])
|
||
|
|
export const UserStatusSchema = z.enum(['active', 'inactive', 'suspended', 'pending'])
|
||
|
|
|
||
|
|
export const UserSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
username: z.string().min(3).max(50),
|
||
|
|
email: z.string().email(),
|
||
|
|
firstName: z.string().min(1).max(100),
|
||
|
|
lastName: z.string().min(1).max(100),
|
||
|
|
displayName: z.string().max(100).optional(),
|
||
|
|
role: UserRoleSchema,
|
||
|
|
status: UserStatusSchema,
|
||
|
|
avatar: z.string().url().optional(),
|
||
|
|
bio: z.string().max(1000).optional(),
|
||
|
|
location: z.string().max(100).optional(),
|
||
|
|
website: z.string().url().optional(),
|
||
|
|
isVerified: z.boolean().default(false),
|
||
|
|
emailVerified: z.boolean().default(false),
|
||
|
|
preferences: z.object({
|
||
|
|
language: z.string().default('fr'),
|
||
|
|
theme: z.enum(['light', 'dark', 'system']).default('system'),
|
||
|
|
notifications: z.object({
|
||
|
|
email: z.boolean().default(true),
|
||
|
|
push: z.boolean().default(true),
|
||
|
|
desktop: z.boolean().default(false),
|
||
|
|
}),
|
||
|
|
privacy: z.object({
|
||
|
|
profileVisibility: z.enum(['public', 'friends', 'private']).default('public'),
|
||
|
|
showOnlineStatus: z.boolean().default(true),
|
||
|
|
allowDirectMessages: z.boolean().default(true),
|
||
|
|
}),
|
||
|
|
}),
|
||
|
|
stats: z.object({
|
||
|
|
tracksUploaded: z.number().default(0),
|
||
|
|
playlistsCreated: z.number().default(0),
|
||
|
|
followersCount: z.number().default(0),
|
||
|
|
followingCount: z.number().default(0),
|
||
|
|
totalPlays: z.number().default(0),
|
||
|
|
}),
|
||
|
|
lastSeenAt: z.date().optional(),
|
||
|
|
createdAt: z.date(),
|
||
|
|
updatedAt: z.date(),
|
||
|
|
})
|
||
|
|
|
||
|
|
// Audio schemas
|
||
|
|
export const AudioGenreSchema = z.enum([
|
||
|
|
'rock', 'pop', 'jazz', 'classical', 'electronic', 'hip-hop', 'country',
|
||
|
|
'blues', 'reggae', 'folk', 'metal', 'punk', 'indie', 'ambient', 'techno'
|
||
|
|
])
|
||
|
|
|
||
|
|
export const AudioFormatSchema = z.enum(['mp3', 'wav', 'flac', 'aac', 'ogg'])
|
||
|
|
export const AudioQualitySchema = z.enum(['low', 'medium', 'high', 'lossless'])
|
||
|
|
|
||
|
|
export const AudioMetadataSchema = z.object({
|
||
|
|
duration: z.number().positive(), // seconds
|
||
|
|
bitrate: z.number().positive(), // kbps
|
||
|
|
sampleRate: z.number().positive(), // Hz
|
||
|
|
channels: z.number().min(1).max(8),
|
||
|
|
format: AudioFormatSchema,
|
||
|
|
quality: AudioQualitySchema,
|
||
|
|
fileSize: z.number().positive(), // bytes
|
||
|
|
bpm: z.number().positive().optional(),
|
||
|
|
key: z.string().optional(), // Musical key
|
||
|
|
loudness: z.number().optional(), // LUFS
|
||
|
|
})
|
||
|
|
|
||
|
|
export const AudioSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
title: z.string().min(1).max(200),
|
||
|
|
slug: z.string(),
|
||
|
|
artist: z.string().min(1).max(100),
|
||
|
|
albumId: z.string().uuid().optional(),
|
||
|
|
albumTitle: z.string().max(200).optional(),
|
||
|
|
genre: AudioGenreSchema,
|
||
|
|
subgenre: z.string().max(50).optional(),
|
||
|
|
tags: z.array(z.string()).default([]),
|
||
|
|
description: z.string().max(2000).optional(),
|
||
|
|
lyrics: z.string().optional(),
|
||
|
|
metadata: AudioMetadataSchema,
|
||
|
|
waveformData: z.array(z.number()).optional(),
|
||
|
|
fileUrl: z.string().url(),
|
||
|
|
coverImageUrl: z.string().url().optional(),
|
||
|
|
uploadedById: z.string().uuid(),
|
||
|
|
isPublic: z.boolean().default(true),
|
||
|
|
isExplicit: z.boolean().default(false),
|
||
|
|
downloadEnabled: z.boolean().default(false),
|
||
|
|
stats: z.object({
|
||
|
|
plays: z.number().default(0),
|
||
|
|
likes: z.number().default(0),
|
||
|
|
shares: z.number().default(0),
|
||
|
|
downloads: z.number().default(0),
|
||
|
|
comments: z.number().default(0),
|
||
|
|
}),
|
||
|
|
releaseDate: z.date().optional(),
|
||
|
|
createdAt: z.date(),
|
||
|
|
updatedAt: z.date(),
|
||
|
|
})
|
||
|
|
|
||
|
|
// Playlist schemas
|
||
|
|
export const PlaylistVisibilitySchema = z.enum(['public', 'unlisted', 'private'])
|
||
|
|
|
||
|
|
export const PlaylistSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
title: z.string().min(1).max(200),
|
||
|
|
slug: z.string(),
|
||
|
|
description: z.string().max(2000).optional(),
|
||
|
|
coverImageUrl: z.string().url().optional(),
|
||
|
|
createdById: z.string().uuid(),
|
||
|
|
visibility: PlaylistVisibilitySchema,
|
||
|
|
isCollaborative: z.boolean().default(false),
|
||
|
|
tags: z.array(z.string()).default([]),
|
||
|
|
trackIds: z.array(z.string().uuid()),
|
||
|
|
stats: z.object({
|
||
|
|
followers: z.number().default(0),
|
||
|
|
plays: z.number().default(0),
|
||
|
|
shares: z.number().default(0),
|
||
|
|
}),
|
||
|
|
createdAt: z.date(),
|
||
|
|
updatedAt: z.date(),
|
||
|
|
})
|
||
|
|
|
||
|
|
// Conversation schemas
|
||
|
|
export const ConversationTypeSchema = z.enum(['direct', 'group', 'channel'])
|
||
|
|
export const MessageTypeSchema = z.enum(['text', 'audio', 'image', 'file', 'system'])
|
||
|
|
export const MessageStatusSchema = z.enum(['sent', 'delivered', 'read', 'failed'])
|
||
|
|
|
||
|
|
export const ConversationSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
name: z.string().max(100).optional(),
|
||
|
|
description: z.string().max(500).optional(),
|
||
|
|
type: ConversationTypeSchema,
|
||
|
|
isPrivate: z.boolean().default(false),
|
||
|
|
avatarUrl: z.string().url().optional(),
|
||
|
|
createdById: z.string().uuid(),
|
||
|
|
participantIds: z.array(z.string().uuid()),
|
||
|
|
lastMessageId: z.string().uuid().optional(),
|
||
|
|
lastActivityAt: z.date(),
|
||
|
|
createdAt: z.date(),
|
||
|
|
updatedAt: z.date(),
|
||
|
|
})
|
||
|
|
|
||
|
|
export const MessageSchema = z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
conversationId: z.string().uuid(),
|
||
|
|
senderId: z.string().uuid(),
|
||
|
|
content: z.string(),
|
||
|
|
type: MessageTypeSchema,
|
||
|
|
status: MessageStatusSchema,
|
||
|
|
parentMessageId: z.string().uuid().optional(), // For replies
|
||
|
|
attachments: z.array(z.object({
|
||
|
|
id: z.string().uuid(),
|
||
|
|
type: z.enum(['image', 'audio', 'video', 'file']),
|
||
|
|
url: z.string().url(),
|
||
|
|
filename: z.string(),
|
||
|
|
size: z.number(),
|
||
|
|
mimeType: z.string(),
|
||
|
|
})).default([]),
|
||
|
|
reactions: z.array(z.object({
|
||
|
|
emoji: z.string(),
|
||
|
|
userId: z.string().uuid(),
|
||
|
|
createdAt: z.date(),
|
||
|
|
})).default([]),
|
||
|
|
mentions: z.array(z.string().uuid()).default([]),
|
||
|
|
isEdited: z.boolean().default(false),
|
||
|
|
isDeleted: z.boolean().default(false),
|
||
|
|
createdAt: z.date(),
|
||
|
|
updatedAt: z.date(),
|
||
|
|
})
|
||
|
|
|
||
|
|
// Export all types
|
||
|
|
export type User = z.infer<typeof UserSchema>
|
||
|
|
export type UserRole = z.infer<typeof UserRoleSchema>
|
||
|
|
export type UserStatus = z.infer<typeof UserStatusSchema>
|
||
|
|
|
||
|
|
export type Audio = z.infer<typeof AudioSchema>
|
||
|
|
export type AudioGenre = z.infer<typeof AudioGenreSchema>
|
||
|
|
export type AudioMetadata = z.infer<typeof AudioMetadataSchema>
|
||
|
|
|
||
|
|
export type Playlist = z.infer<typeof PlaylistSchema>
|
||
|
|
export type PlaylistVisibility = z.infer<typeof PlaylistVisibilitySchema>
|
||
|
|
|
||
|
|
export type Conversation = z.infer<typeof ConversationSchema>
|
||
|
|
export type ConversationType = z.infer<typeof ConversationTypeSchema>
|
||
|
|
|
||
|
|
export type Message = z.infer<typeof MessageSchema>
|
||
|
|
export type MessageType = z.infer<typeof MessageTypeSchema>
|
||
|
|
export type MessageStatus = z.infer<typeof MessageStatusSchema>
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validation utilities
|
||
|
|
*/
|
||
|
|
export const validateUser = (data: unknown): User => UserSchema.parse(data)
|
||
|
|
export const validateAudio = (data: unknown): Audio => AudioSchema.parse(data)
|
||
|
|
export const validatePlaylist = (data: unknown): Playlist => PlaylistSchema.parse(data)
|
||
|
|
export const validateConversation = (data: unknown): Conversation => ConversationSchema.parse(data)
|
||
|
|
export const validateMessage = (data: unknown): Message => MessageSchema.parse(data)
|