/** * Database Model Template * * Usage: Copy this file to create new database models * 1. Replace TEMPLATE_NAME with your model name * 2. Define the Zod schema for validation * 3. Create the Mongoose schema * 4. Add methods and statics as needed */ import mongoose, { Document, Model } from 'mongoose' import { z } from 'zod' // Zod schema for validation export const TemplateSchema = z.object({ // Define your fields here name: z.string().min(1, 'Name is required').max(100, 'Name too long'), description: z.string().optional(), category: z.enum(['type1', 'type2', 'type3']).default('type1'), tags: z.array(z.string()).optional(), isActive: z.boolean().optional().default(true), metadata: z.record(z.string(), z.any()).optional(), // Relationships userId: z.string().min(1, 'User ID is required'), parentId: z.string().optional(), // Nested objects settings: z .object({ visibility: z.enum(['public', 'private', 'unlisted']).default('public'), allowComments: z.boolean().default(true), featured: z.boolean().default(false), }) .optional(), // Arrays of objects items: z .array( z.object({ title: z.string(), value: z.union([z.string(), z.number()]), order: z.number().default(0), }) ) .optional(), }) // Infer TypeScript type from Zod schema export type TemplateType = z.infer // Mongoose document interface export interface ITemplate extends Document { name: string description?: string category: 'type1' | 'type2' | 'type3' tags?: string[] isActive: boolean metadata?: Record // Relationships userId: string parentId?: string // Nested objects settings?: { visibility: 'public' | 'private' | 'unlisted' allowComments: boolean featured: boolean } // Arrays of objects items?: Array<{ title: string value: string | number order: number }> // Timestamps (added by Mongoose) createdAt: Date updatedAt: Date // Instance methods toPublicJSON(): Partial isOwnedBy(userId: string): boolean activate(): Promise deactivate(): Promise addTag(tag: string): Promise removeTag(tag: string): Promise } // Mongoose schema const templateSchema = new mongoose.Schema( { name: { type: String, required: true, trim: true, maxlength: 100, }, description: { type: String, trim: true, maxlength: 500, }, category: { type: String, enum: ['type1', 'type2', 'type3'], default: 'type1', }, tags: [ { type: String, trim: true, lowercase: true, }, ], isActive: { type: Boolean, default: true, }, metadata: { type: mongoose.Schema.Types.Mixed, default: {}, }, // Relationships userId: { type: String, required: true, index: true, }, parentId: { type: String, index: true, }, // Nested objects settings: { visibility: { type: String, enum: ['public', 'private', 'unlisted'], default: 'public', }, allowComments: { type: Boolean, default: true, }, featured: { type: Boolean, default: false, }, }, // Arrays of objects items: [ { title: { type: String, required: true, }, value: { type: mongoose.Schema.Types.Mixed, required: true, }, order: { type: Number, default: 0, }, }, ], }, { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true }, } ) // Indexes for performance templateSchema.index({ userId: 1, isActive: 1 }) templateSchema.index({ category: 1, isActive: 1 }) templateSchema.index({ tags: 1 }) templateSchema.index({ createdAt: -1 }) templateSchema.index({ 'settings.featured': 1, isActive: 1 }) // Text search index templateSchema.index({ name: 'text', description: 'text', tags: 'text', }) // Virtual fields templateSchema.virtual('itemCount').get(function () { return this.items?.length || 0 }) templateSchema.virtual('url').get(function () { return `/templates/${this._id}` }) // Instance methods templateSchema.methods.toPublicJSON = function (): Partial { const obj = this.toObject() // Remove sensitive fields delete obj.metadata delete obj.__v return obj } templateSchema.methods.isOwnedBy = function (userId: string): boolean { return this.userId === userId } templateSchema.methods.activate = async function (): Promise { this.isActive = true return this.save() } templateSchema.methods.deactivate = async function (): Promise { this.isActive = false return this.save() } templateSchema.methods.addTag = async function (tag: string): Promise { const normalizedTag = tag.toLowerCase().trim() if (!this.tags?.includes(normalizedTag)) { if (!this.tags) this.tags = [] this.tags.push(normalizedTag) return this.save() } return this } templateSchema.methods.removeTag = async function (tag: string): Promise { const normalizedTag = tag.toLowerCase().trim() if (this.tags?.includes(normalizedTag)) { this.tags = this.tags.filter((t) => t !== normalizedTag) return this.save() } return this } // Static methods templateSchema.statics.findByUser = function (userId: string) { return this.find({ userId, isActive: true }).sort({ createdAt: -1 }) } templateSchema.statics.findFeatured = function (limit: number = 10) { return this.find({ 'settings.featured': true, isActive: true, 'settings.visibility': 'public', }) .limit(limit) .sort({ createdAt: -1 }) } templateSchema.statics.findByCategory = function (category: string) { return this.find({ category, isActive: true, 'settings.visibility': 'public', }).sort({ createdAt: -1 }) } templateSchema.statics.search = function (query: string, limit: number = 20) { return this.find( { $text: { $search: query }, isActive: true, 'settings.visibility': 'public', }, { score: { $meta: 'textScore' } } ) .sort({ score: { $meta: 'textScore' } }) .limit(limit) } // Pre-save middleware templateSchema.pre('save', function (next) { // Normalize tags if (this.tags) { this.tags = this.tags.map((tag) => tag.toLowerCase().trim()).filter(Boolean) // Remove duplicates this.tags = [...new Set(this.tags)] } // Sort items by order if (this.items && this.items.length > 0) { this.items.sort((a, b) => a.order - b.order) } next() }) // Post-save middleware templateSchema.post('save', function (doc) { console.log(`Template saved: ${doc.name} (${doc._id})`) }) // Model interface with static methods interface ITemplateModel extends Model { findByUser(userId: string): Promise findFeatured(limit?: number): Promise findByCategory(category: string): Promise search(query: string, limit?: number): Promise } // Export the model export const Template = (mongoose.models.Template || mongoose.model('Template', templateSchema)) as ITemplateModel /** * Usage Examples: * * 1. Create a new template: * const template = new Template({ * name: 'My Template', * userId: 'user123', * category: 'type1', * }); * await template.save(); * * 2. Find user's templates: * const templates = await Template.findByUser('user123'); * * 3. Search templates: * const results = await Template.search('react component'); * * 4. Validate data before saving: * try { * const validData = TemplateSchema.parse(inputData); * const template = new Template(validData); * await template.save(); * } catch (error) { * // Handle validation error * } */