import { Injectable, Logger, OnModuleInit } from "@nestjs/common"; import { InjectModel } from "@nestjs/mongoose"; import { FilterQuery, Model, UpdateQuery } from "mongoose"; import { ClientModel, ClientDocument } from "../schema/client.schema"; @Injectable() export class ClientDbService implements OnModuleInit { private readonly logger = new Logger(ClientDbService.name); constructor( @InjectModel(ClientModel.name) private readonly clientModel: Model, ) {} async onModuleInit(): Promise { await this.dropLegacyPropertyUniqueIndex(); } /** * `property` was once declared `unique: true` in the schema. Production keeps * `autoIndex: false`, so the stale unique index remained and rejected a second * client with a missing/null SMS key (E11000 duplicate key). */ private async dropLegacyPropertyUniqueIndex(): Promise { try { const indexes = await this.clientModel.collection.indexes(); for (const index of indexes) { if (!index.unique || !index.name || index.name === "_id_") continue; const keys = Object.keys(index.key ?? {}); const isLegacyPropertyIndex = keys.length === 1 && (keys[0] === "property" || keys[0] === "property.smsApiKey"); if (!isLegacyPropertyIndex) continue; await this.clientModel.collection.dropIndex(index.name); this.logger.warn( `Dropped legacy unique index on clients.${keys[0]}: ${index.name}`, ); } } catch (err) { this.logger.error("Failed to drop legacy client property index", err); } } async create(client: ClientModel): Promise { return await this.clientModel.create(client); } async find(client: FilterQuery): Promise { return await this.clientModel.findOne(client); } async findOne(client: FilterQuery) { return this.clientModel.findOne(client); } async findAll(): Promise { return await this.clientModel.find(); } async findByIdAndUpdate( id: string, update: UpdateQuery, ): Promise { return this.clientModel .findByIdAndUpdate(id, update, { new: true }) .lean() .exec(); } }