import mongoose, { Schema, Document, Model } from 'mongoose'; import { CONFIG } from '../config'; import { logger } from '../utils/logger'; // ── Connection ──────────────────────────────────────────────────────────────── let isConnected = false; export async function connectMongo(): Promise { if (isConnected) return; mongoose.set('strictQuery', true); await mongoose.connect(CONFIG.MONGO_URI, { maxPoolSize: 10, // Conserve connections on free tier serverSelectionTimeoutMS: 5000, socketTimeoutMS: 30000, }); isConnected = true; logger.info('MongoDB connected'); mongoose.connection.on('error', (err: Error) => logger.error('MongoDB connection error', { error: err.message }) ); mongoose.connection.on('disconnected', () => { isConnected = false; logger.warn('MongoDB disconnected'); }); } // ── User Profile Model ──────────────────────────────────────────────────────── export interface IUser extends Document { uid: string; displayName: string; avatarUrl?: string; friends: string[]; // Array of UIDs blockedUsers: string[]; settings: { notifications: boolean; theme: 'light' | 'dark'; }; createdAt: Date; updatedAt: Date; } const UserSchema = new Schema( { uid: { type: String, required: true, unique: true, index: true }, displayName: { type: String, required: true, maxlength: 64 }, avatarUrl: { type: String, default: null }, friends: { type: [String], default: [] }, blockedUsers: { type: [String], default: [] }, settings: { notifications: { type: Boolean, default: true }, theme: { type: String, enum: ['light', 'dark'], default: 'dark' }, }, }, { timestamps: true, // Lean queries by default for read-heavy paths } ); // Compound index for friend-list lookups UserSchema.index({ uid: 1, friends: 1 }); export const UserModel: Model = mongoose.model('User', UserSchema); // ── Persistent Message Log (optional, for history) ──────────────────────────── export interface IMessage extends Document { fromUid: string; toUid: string; type: 'text' | 'media'; content: string; // Text body or Supabase URL mediaType?: string; // MIME type for media messages delivered: boolean; createdAt: Date; } const MessageSchema = new Schema( { fromUid: { type: String, required: true, index: true }, toUid: { type: String, required: true, index: true }, type: { type: String, enum: ['text', 'media'], required: true }, content: { type: String, required: true, maxlength: 4096 }, mediaType: { type: String, default: null }, delivered: { type: Boolean, default: false, index: true }, }, { timestamps: true, // TTL index: auto-delete messages older than 30 days to save Atlas space expireAfterSeconds: 60 * 60 * 24 * 30, } ); MessageSchema.index({ fromUid: 1, toUid: 1, createdAt: -1 }); export const MessageModel: Model = mongoose.model( 'Message', MessageSchema );