@@ -175,10 +175,16 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
175175 // 获取用户的邮箱列表
176176 if ( path === '/api/mailboxes' && request . method === 'GET' ) {
177177 if ( isMock ) {
178+ const searchParam = url . searchParams . get ( 'q' ) ;
178179 const domainParam = url . searchParams . get ( 'domain' ) ;
179180 const favoriteParam = url . searchParams . get ( 'favorite' ) ;
180181 const forwardParam = url . searchParams . get ( 'forward' ) ;
181182 let results = buildMockMailboxes ( MOCK_DOMAINS ) ;
183+ // 搜索过滤
184+ if ( searchParam && searchParam . trim ( ) ) {
185+ const q = searchParam . trim ( ) . toLowerCase ( ) ;
186+ results = results . filter ( m => m . address . toLowerCase ( ) . includes ( q ) ) ;
187+ }
182188 if ( domainParam ) {
183189 results = results . filter ( m => m . address . endsWith ( '@' + domainParam ) ) ;
184190 }
@@ -192,7 +198,15 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
192198 } else if ( forwardParam === 'false' || forwardParam === '0' ) {
193199 results = results . filter ( m => ! m . forward_to ) ;
194200 }
195- return Response . json ( results ) ;
201+ // 分页
202+ const pageParam = url . searchParams . get ( 'page' ) ;
203+ const sizeParam = url . searchParams . get ( 'size' ) ;
204+ const page = Math . max ( 1 , Number ( pageParam || 1 ) ) ;
205+ const size = Math . max ( 1 , Math . min ( 500 , Number ( sizeParam || 20 ) ) ) ;
206+ const total = results . length ;
207+ const start = ( page - 1 ) * size ;
208+ const pageResult = results . slice ( start , start + size ) ;
209+ return Response . json ( { list : pageResult , total } ) ;
196210 }
197211
198212 const payload = getJwtPayload ( request , options ) ;
@@ -209,9 +223,9 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
209223 WHERE address = ?
210224 LIMIT 1
211225 ` ) . bind ( payload . mailboxAddress ) . all ( ) ;
212- return Response . json ( results || [ ] ) ;
226+ return Response . json ( { list : results || [ ] , total : results ?. length || 0 } ) ;
213227 } catch ( e ) {
214- return Response . json ( [ ] ) ;
228+ return Response . json ( { list : [ ] , total : 0 } ) ;
215229 }
216230 }
217231
@@ -232,10 +246,24 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
232246 }
233247 }
234248
235- if ( ! uid && ! strictAdmin ) return Response . json ( [ ] ) ;
249+ if ( ! uid && ! strictAdmin ) return Response . json ( { list : [ ] , total : 0 } ) ;
236250
237- const limit = Math . max ( 1 , Math . min ( 500 , Number ( url . searchParams . get ( 'limit' ) || 100 ) ) ) ;
238- const offset = Math . max ( 0 , Number ( url . searchParams . get ( 'offset' ) || 0 ) ) ;
251+ // 支持两种分页参数:page/size 或 limit/offset
252+ let limit , offset ;
253+ const pageParam = url . searchParams . get ( 'page' ) ;
254+ const sizeParam = url . searchParams . get ( 'size' ) ;
255+
256+ if ( pageParam !== null || sizeParam !== null ) {
257+ // 使用 page/size 分页
258+ const page = Math . max ( 1 , Number ( pageParam || 1 ) ) ;
259+ const size = Math . max ( 1 , Math . min ( 500 , Number ( sizeParam || 20 ) ) ) ;
260+ limit = size ;
261+ offset = ( page - 1 ) * size ;
262+ } else {
263+ // 使用 limit/offset 分页
264+ limit = Math . max ( 1 , Math . min ( 500 , Number ( url . searchParams . get ( 'limit' ) || 100 ) ) ) ;
265+ offset = Math . max ( 0 , Number ( url . searchParams . get ( 'offset' ) || 0 ) ) ;
266+ }
239267
240268 const bindParams = [ ] ;
241269 const whereConditions = [ ] ;
@@ -247,11 +275,18 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
247275 bindParams . push ( uid ) ;
248276 }
249277
278+ const searchParam = url . searchParams . get ( 'q' ) ;
250279 const domainParam = url . searchParams . get ( 'domain' ) ;
251280 const loginParam = url . searchParams . get ( 'login' ) ;
252281 const favoriteParam = url . searchParams . get ( 'favorite' ) ;
253282 const forwardParam = url . searchParams . get ( 'forward' ) ;
254283
284+ // 搜索过滤(模糊匹配邮箱地址)
285+ if ( searchParam && searchParam . trim ( ) ) {
286+ whereConditions . push ( 'm.address LIKE ?' ) ;
287+ bindParams . push ( `%${ searchParam . trim ( ) . toLowerCase ( ) } %` ) ;
288+ }
289+
255290 if ( domainParam ) {
256291 whereConditions . push ( 'm.domain = ?' ) ;
257292 bindParams . push ( domainParam ) ;
@@ -281,11 +316,25 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
281316 const whereClause = whereConditions . length > 0 ? 'WHERE ' + whereConditions . join ( ' AND ' ) : '' ;
282317 bindParams . push ( limit , offset ) ;
283318
319+ // 构建计数查询的参数(不包含 limit 和 offset)
320+ const countBindParams = bindParams . slice ( 0 , - 2 ) ;
321+
284322 // 严格管理员使用 LEFT JOIN 显示所有邮箱,同时保留自己的置顶状态
285323 // 普通用户使用 INNER JOIN 只显示自己关联的邮箱
286324 if ( strictAdmin && uid ) {
287325 // 严格管理员:显示所有邮箱,使用 LEFT JOIN 获取自己的置顶状态
288326 const adminBindParams = [ uid , ...bindParams ] ;
327+ const adminCountBindParams = [ uid , ...countBindParams ] ;
328+
329+ // 获取总数
330+ const countResult = await db . prepare ( `
331+ SELECT COUNT(*) as total
332+ FROM mailboxes m
333+ LEFT JOIN user_mailboxes um ON m.id = um.mailbox_id AND um.user_id = ?
334+ ${ whereClause }
335+ ` ) . bind ( ...adminCountBindParams ) . first ( ) ;
336+ const total = countResult ?. total || 0 ;
337+
289338 const { results } = await db . prepare ( `
290339 SELECT m.id, m.address, m.created_at, COALESCE(um.is_pinned, 0) AS is_pinned,
291340 CASE WHEN (m.password_hash IS NULL OR m.password_hash = '') THEN 1 ELSE 0 END AS password_is_default,
@@ -297,9 +346,17 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
297346 ORDER BY COALESCE(um.is_pinned, 0) DESC, m.created_at DESC
298347 LIMIT ? OFFSET ?
299348 ` ) . bind ( ...adminBindParams ) . all ( ) ;
300- return Response . json ( results || [ ] ) ;
349+ return Response . json ( { list : results || [ ] , total } ) ;
301350 } else if ( strictAdmin ) {
302351 // 严格管理员但没有 uid(不应该发生,但作为兜底)
352+ // 获取总数
353+ const countResult = await db . prepare ( `
354+ SELECT COUNT(*) as total
355+ FROM mailboxes m
356+ ${ whereClause }
357+ ` ) . bind ( ...countBindParams ) . first ( ) ;
358+ const total = countResult ?. total || 0 ;
359+
303360 const { results } = await db . prepare ( `
304361 SELECT m.id, m.address, m.created_at, 0 AS is_pinned,
305362 CASE WHEN (m.password_hash IS NULL OR m.password_hash = '') THEN 1 ELSE 0 END AS password_is_default,
@@ -310,9 +367,18 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
310367 ORDER BY m.created_at DESC
311368 LIMIT ? OFFSET ?
312369 ` ) . bind ( ...bindParams ) . all ( ) ;
313- return Response . json ( results || [ ] ) ;
370+ return Response . json ( { list : results || [ ] , total } ) ;
314371 } else {
315372 // 普通用户:只显示自己关联的邮箱
373+ // 获取总数
374+ const countResult = await db . prepare ( `
375+ SELECT COUNT(*) as total
376+ FROM user_mailboxes um
377+ JOIN mailboxes m ON m.id = um.mailbox_id
378+ ${ whereClause }
379+ ` ) . bind ( ...countBindParams ) . first ( ) ;
380+ const total = countResult ?. total || 0 ;
381+
316382 const { results } = await db . prepare ( `
317383 SELECT m.id, m.address, m.created_at, um.is_pinned,
318384 CASE WHEN (m.password_hash IS NULL OR m.password_hash = '') THEN 1 ELSE 0 END AS password_is_default,
@@ -324,10 +390,10 @@ export async function handleMailboxesApi(request, db, mailDomains, url, path, op
324390 ORDER BY um.is_pinned DESC, m.created_at DESC
325391 LIMIT ? OFFSET ?
326392 ` ) . bind ( ...bindParams ) . all ( ) ;
327- return Response . json ( results || [ ] ) ;
393+ return Response . json ( { list : results || [ ] , total } ) ;
328394 }
329395 } catch ( _ ) {
330- return Response . json ( [ ] ) ;
396+ return Response . json ( { list : [ ] , total : 0 } ) ;
331397 }
332398 }
333399
0 commit comments