mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-04 06:56:45 +08:00
perf: paginate user addresses and optimize ownership queries (#1105)
* perf: paginate user addresses and optimize mail ownership queries * docs: document user address pagination * fix: address pagination review feedback * fix: cover user address pagination flows * test: fix user mailbox tab selector * test: stabilize remote address search flow * test: stabilize user address browser flow * fix: preserve address pagination compatibility * refactor: simplify bound address query types * refactor: reuse list query for bound addresses * fix: preserve paginated address totals * fix: preserve bound address helper contracts * fix: require pagination for user addresses * refactor: keep shared pagination behavior unchanged * fix: align pagination docs and tests * fix: clear stale address selections * refactor: simplify user address pagination * refactor: limit user address changes to pagination * test: select a visible mailbox address * fix: preserve bound address response fields
This commit is contained in:
@@ -4,7 +4,7 @@ import { Jwt } from 'hono/utils/jwt'
|
||||
import { isAddressCountLimitReached } from "../utils"
|
||||
import { unbindTelegramByAddress } from '../telegram_api/common';
|
||||
import i18n from '../i18n';
|
||||
import { updateAddressUpdatedAt, commonGetUserRole, hideObjectFields } from '../common';
|
||||
import { updateAddressUpdatedAt, commonGetUserRole, handleListQuery, hideObjectFields } from '../common';
|
||||
|
||||
const UserBindAddressModule = {
|
||||
bind: async (c: Context<HonoCustomType>) => {
|
||||
@@ -97,16 +97,24 @@ const UserBindAddressModule = {
|
||||
},
|
||||
getBindedAddresses: async (c: Context<HonoCustomType>) => {
|
||||
const { user_id } = c.get("userPayload");
|
||||
const results = await UserBindAddressModule.getBindedAddressesById(c, user_id);
|
||||
return c.json({
|
||||
results: results,
|
||||
});
|
||||
},
|
||||
getBindedAddressListById: async (
|
||||
c: Context<HonoCustomType>, user_id: number | string
|
||||
): Promise<string[]> => {
|
||||
const bindedAddressList = await UserBindAddressModule.getBindedAddressesById(c, user_id);
|
||||
return bindedAddressList.map((item) => item.name);
|
||||
const { limit, offset } = c.req.query();
|
||||
const params = [String(user_id)];
|
||||
const fromQuery = ` FROM address a`
|
||||
+ ` JOIN users_address ua ON ua.address_id = a.id`
|
||||
+ ` WHERE ua.user_id = ?`;
|
||||
return await handleListQuery(
|
||||
c,
|
||||
`SELECT a.*,`
|
||||
+ ` (SELECT COUNT(*) FROM raw_mails WHERE address = a.name) AS mail_count,`
|
||||
+ ` (SELECT COUNT(*) FROM sendbox WHERE address = a.name) AS send_count`
|
||||
+ fromQuery,
|
||||
`SELECT COUNT(*) AS count${fromQuery}`,
|
||||
params,
|
||||
limit ?? 20,
|
||||
offset ?? 0,
|
||||
'a.id DESC',
|
||||
['password'],
|
||||
);
|
||||
},
|
||||
getBindedAddressesById: async (
|
||||
c: Context<HonoCustomType>, user_id: number | string
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import { Context } from "hono";
|
||||
import i18n from "../i18n";
|
||||
import { handleMailListQuery } from "../common";
|
||||
import UserBindAddressModule from "./bind_address";
|
||||
import { getBooleanValue } from "../utils";
|
||||
|
||||
export default {
|
||||
getMails: async (c: Context<HonoCustomType>) => {
|
||||
const { user_id } = c.get("userPayload");
|
||||
const { address, limit, offset } = c.req.query();
|
||||
const bindedAddressList = await UserBindAddressModule.getBindedAddressListById(c, user_id);
|
||||
const addressList = address ? bindedAddressList.filter((item) => item == address) : bindedAddressList;
|
||||
const addressQuery = `address IN (${addressList.map(() => "?").join(",")})`;
|
||||
const addressParams = addressList;
|
||||
|
||||
// user must have at least one binded address to query mails
|
||||
if (addressList.length <= 0) {
|
||||
return c.json({ results: [], count: 0 });
|
||||
const filterQuerys = [`ua.user_id = ?`];
|
||||
const filterParams = [String(user_id)];
|
||||
if (address) {
|
||||
filterQuerys.push(`rm.address = ?`);
|
||||
filterParams.push(address);
|
||||
}
|
||||
|
||||
const filterQuerys = [addressQuery].filter((item) => item).join(" and ");
|
||||
const finalQuery = filterQuerys.length > 0 ? `where ${filterQuerys}` : "";
|
||||
const filterParams = [...addressParams]
|
||||
const fromQuery = ` FROM users_address ua`
|
||||
+ ` JOIN address a ON a.id = ua.address_id`
|
||||
+ ` JOIN raw_mails rm ON rm.address = a.name`
|
||||
+ ` WHERE ${filterQuerys.join(" AND ")}`;
|
||||
return await handleMailListQuery(c,
|
||||
`SELECT * FROM raw_mails ${finalQuery}`,
|
||||
`SELECT count(*) as count FROM raw_mails ${finalQuery}`,
|
||||
filterParams, limit, offset
|
||||
`SELECT rm.*${fromQuery}`,
|
||||
`SELECT count(*) as count${fromQuery}`,
|
||||
filterParams, limit, offset, 'rm.id desc'
|
||||
);
|
||||
},
|
||||
deleteMail: async (c: Context<HonoCustomType>) => {
|
||||
@@ -34,11 +30,14 @@ export default {
|
||||
}
|
||||
const { id } = c.req.param();
|
||||
const { user_id } = c.get("userPayload");
|
||||
const bindedAddressList = await UserBindAddressModule.getBindedAddressListById(c, user_id);
|
||||
const { success } = await c.env.DB.prepare(
|
||||
`DELETE FROM raw_mails WHERE id = ?`
|
||||
+ ` and address IN (${bindedAddressList.map(() => "?").join(",")})`
|
||||
).bind(id, ...bindedAddressList).run();
|
||||
+ ` AND EXISTS (`
|
||||
+ `SELECT 1 FROM users_address ua`
|
||||
+ ` JOIN address a ON a.id = ua.address_id`
|
||||
+ ` WHERE ua.user_id = ? AND a.name = raw_mails.address`
|
||||
+ `)`
|
||||
).bind(id, user_id).run();
|
||||
return c.json({
|
||||
success: success
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user