chatapi / src /db /mongo.ts
wilooper's picture
initial commit
87cb242
Raw History Blame Contribute Delete
3.37 kB
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<void> {
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<IUser>(
{
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<IUser> = mongoose.model<IUser>('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<IMessage>(
{
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<IMessage> = mongoose.model<IMessage>(
'Message',
MessageSchema
);