@@ -24,6 +24,8 @@ const url = process.env.MONGODB_URL
2424
2525let client : MongoClient
2626let dbName : string
27+ let isInitialized = false
28+
2729try {
2830 client = new MongoClient ( url )
2931 const parsedUrl = new URL ( url )
@@ -53,6 +55,157 @@ const userPromptCol = client.db(dbName).collection<UserPrompt>('user_prompt')
5355// }
5456const redeemCol = client . db ( dbName ) . collection < GiftCard > ( 'giftcards' )
5557
58+ /**
59+ * Initialize all database indexes
60+ * This should be called once when the application starts
61+ * Note: createIndex is idempotent - it won't fail if index already exists
62+ */
63+
64+ async function initializeIndexes ( ) {
65+ try {
66+ // ============================================
67+ // chat_room collection indexes
68+ // ============================================
69+ // Index for /api/chatrooms: getChatRooms(userId)
70+ // Query: { userId, status: { $ne: Status.Deleted } }
71+ await roomCol . createIndex ( { userId : 1 , status : 1 } , { name : 'idx_userId_status' } )
72+
73+ // Index for getChatRoom: { userId, roomId, status: { $ne: Status.Deleted } }
74+ await roomCol . createIndex ( { userId : 1 , roomId : 1 , status : 1 } , { name : 'idx_userId_roomId_status' } )
75+
76+ // Index for existsChatRoom: { roomId, userId }
77+ await roomCol . createIndex ( { roomId : 1 , userId : 1 } , { name : 'idx_roomId_userId' } )
78+
79+ // Index for getChatRoomsCount aggregation lookup
80+ await roomCol . createIndex ( { roomId : 1 } , { name : 'idx_roomId' } )
81+
82+ globalThis . console . log ( '✓ chat_room collection indexes created' )
83+
84+ // ============================================
85+ // chat collection indexes
86+ // ============================================
87+ // Index for /api/chat-history: getChats(roomId, lastId, all)
88+ // Query: { roomId, uuid: { $lt: lastId }, status: { $ne: Status.Deleted } }
89+ // Sort: { dateTime: -1 }
90+ await chatCol . createIndex (
91+ { roomId : 1 , uuid : - 1 , dateTime : - 1 , status : 1 } ,
92+ { name : 'idx_roomId_uuid_dateTime_status' } ,
93+ )
94+
95+ // Alternative index for queries without status filter
96+ await chatCol . createIndex (
97+ { roomId : 1 , uuid : - 1 , dateTime : - 1 } ,
98+ { name : 'idx_roomId_uuid_dateTime' } ,
99+ )
100+
101+ // Index for getChat: { roomId, uuid }
102+ await chatCol . createIndex ( { roomId : 1 , uuid : 1 } , { name : 'idx_roomId_uuid' } )
103+
104+ // Index for getChatByMessageId: { 'options.messageId': messageId }
105+ await chatCol . createIndex ( { 'options.messageId' : 1 } , { name : 'idx_options_messageId' } )
106+
107+ // Index for getChatRoomsCount aggregation lookup
108+ await chatCol . createIndex ( { roomId : 1 , dateTime : - 1 } , { name : 'idx_roomId_dateTime' } )
109+
110+ // Index for deleteAllChatRooms: updateMany({ userId, status: Status.Normal }, ...)
111+ await chatCol . createIndex ( { userId : 1 , status : 1 } , { name : 'idx_userId_status' } )
112+
113+ globalThis . console . log ( '✓ chat collection indexes created' )
114+
115+ // ============================================
116+ // user collection indexes
117+ // ============================================
118+ // Index for getUser: { email }
119+ try {
120+ await userCol . createIndex ( { email : 1 } , { name : 'idx_email' , unique : true } )
121+ }
122+ catch ( error : any ) {
123+ // Ignore error if unique index already exists
124+ if ( ! error . message ?. includes ( 'E11000' ) && ! error . message ?. includes ( 'duplicate key' ) ) {
125+ throw error
126+ }
127+ }
128+
129+ // Index for getUsers: { status: { $ne: Status.Deleted } } with sort by createTime
130+ await userCol . createIndex ( { status : 1 , createTime : - 1 } , { name : 'idx_status_createTime' } )
131+
132+ globalThis . console . log ( '✓ user collection indexes created' )
133+
134+ // ============================================
135+ // chat_usage collection indexes
136+ // ============================================
137+ // Index for getUserStatisticsByDay: { dateTime, userId }
138+ await usageCol . createIndex ( { dateTime : 1 , userId : 1 } , { name : 'idx_dateTime_userId' } )
139+
140+ globalThis . console . log ( '✓ chat_usage collection indexes created' )
141+
142+ // ============================================
143+ // giftcards collection indexes
144+ // ============================================
145+ // Index for getAmtByCardNo: { cardno }
146+ try {
147+ await redeemCol . createIndex ( { cardno : 1 } , { name : 'idx_cardno' , unique : true } )
148+ }
149+ catch ( error : any ) {
150+ // Ignore error if unique index already exists
151+ if ( ! error . message ?. includes ( 'E11000' ) && ! error . message ?. includes ( 'duplicate key' ) ) {
152+ throw error
153+ }
154+ }
155+
156+ globalThis . console . log ( '✓ giftcards collection indexes created' )
157+
158+ // ============================================
159+ // user_prompt collection indexes
160+ // ============================================
161+ // Index for getUserPromptList: { userId }
162+ await userPromptCol . createIndex ( { userId : 1 } , { name : 'idx_userId' } )
163+
164+ globalThis . console . log ( '✓ user_prompt collection indexes created' )
165+
166+ // ============================================
167+ // key_config collection indexes
168+ // ============================================
169+ // Index for getKeys: { status: { $ne: Status.Disabled } }
170+ await keyCol . createIndex ( { status : 1 } , { name : 'idx_status' } )
171+
172+ globalThis . console . log ( '✓ key_config collection indexes created' )
173+
174+ globalThis . console . log ( '✓ All database indexes initialized successfully' )
175+ }
176+ catch ( error : any ) {
177+ // Log error but don't throw - allow application to start even if index creation fails
178+ globalThis . console . error ( '⚠ Warning: Error initializing database indexes:' , error . message )
179+ globalThis . console . error ( ' Application will continue to start. You may need to create indexes manually.' )
180+ }
181+ }
182+
183+ /**
184+ * Initialize MongoDB connection and indexes
185+ * This should be called once when the application starts
186+ */
187+ export async function initializeMongoDB ( ) {
188+ if ( isInitialized ) {
189+ return
190+ }
191+
192+ try {
193+ // Connect to MongoDB
194+ await client . connect ( )
195+ globalThis . console . log ( '✓ MongoDB connected successfully' )
196+
197+ // Initialize indexes
198+ await initializeIndexes ( )
199+
200+ isInitialized = true
201+ }
202+ catch ( error : any ) {
203+ globalThis . console . error ( '✗ Error initializing MongoDB:' , error . message )
204+ // Don't throw - allow application to continue
205+ // MongoDB operations will fail gracefully if connection is not established
206+ }
207+ }
208+
56209/**
57210 * 插入聊天信息
58211 * @param uuid
0 commit comments