refactor: decouple mail states from gzip

This commit is contained in:
dreamhunter2333
2026-08-26 00:28:33 +08:00
parent b809a5489f
commit fe73bbc54c
6 changed files with 22 additions and 23 deletions
+3 -2
View File
@@ -2,6 +2,7 @@ import { Context } from "hono";
import { handleMailListQuery } from "../common"; import { handleMailListQuery } from "../common";
import { resolveRawEmailRow } from "../gzip"; import { resolveRawEmailRow } from "../gzip";
import { getBooleanValue } from "../utils"; import { getBooleanValue } from "../utils";
import { serializeMailState } from "../mail_flags";
export default { export default {
getMails: async (c: Context<HonoCustomType>) => { getMails: async (c: Context<HonoCustomType>) => {
@@ -32,8 +33,8 @@ export default {
`SELECT * FROM raw_mails WHERE id = ?` `SELECT * FROM raw_mails WHERE id = ?`
).bind(id).first(); ).bind(id).first();
if (!result) return c.json(null); if (!result) return c.json(null);
return c.json(await resolveRawEmailRow( return c.json(serializeMailState(
result, await resolveRawEmailRow(result),
getBooleanValue(c.env.ENABLE_MAIL_FLAGS), getBooleanValue(c.env.ENABLE_MAIL_FLAGS),
)); ));
}, },
+6 -4
View File
@@ -7,6 +7,7 @@ import { unbindTelegramByAddress } from './telegram_api/common';
import { CONSTANTS } from './constants'; import { CONSTANTS } from './constants';
import { AddressCreationSettings, AdminWebhookSettings, ExtractResult, WebhookMail, WebhookSettings } from './models'; import { AddressCreationSettings, AdminWebhookSettings, ExtractResult, WebhookMail, WebhookSettings } from './models';
import i18n from './i18n'; import i18n from './i18n';
import { serializeMailState } from './mail_flags';
const DEFAULT_NAME_REGEX = /[^a-z0-9]/g; const DEFAULT_NAME_REGEX = /[^a-z0-9]/g;
const DEFAULT_RANDOM_SUBDOMAIN_LENGTH = 8; const DEFAULT_RANDOM_SUBDOMAIN_LENGTH = 8;
@@ -720,14 +721,15 @@ export const handleMailListQuery = async (
const { results } = await c.env.DB.prepare(resultsQuery).bind( const { results } = await c.env.DB.prepare(resultsQuery).bind(
...params, limit, offset ...params, limit, offset
).all(); ).all();
const resolvedResults = await resolveRawEmailList( const resolvedResults = await resolveRawEmailList(results);
results, const serializedResults = resolvedResults.map(row => serializeMailState(
row,
getBooleanValue(c.env.ENABLE_MAIL_FLAGS), getBooleanValue(c.env.ENABLE_MAIL_FLAGS),
); ));
const count = offset == 0 ? await c.env.DB.prepare( const count = offset == 0 ? await c.env.DB.prepare(
countQuery countQuery
).bind(...params).first("count") : 0; ).bind(...params).first("count") : 0;
return c.json({ results: resolvedResults, count }); return c.json({ results: serializedResults, count });
} }
export const commonParseMail = async (parsedEmailContext: ParsedEmailContext): Promise<{ export const commonParseMail = async (parsedEmailContext: ParsedEmailContext): Promise<{
+4 -11
View File
@@ -4,7 +4,6 @@
*/ */
import { RawMailRow } from "./models"; import { RawMailRow } from "./models";
import { serializeMailState } from "./mail_flags";
export async function compressText(text: string): Promise<ArrayBuffer> { export async function compressText(text: string): Promise<ArrayBuffer> {
const stream = new Blob([text]).stream().pipeThrough(new CompressionStream('gzip')); const stream = new Blob([text]).stream().pipeThrough(new CompressionStream('gzip'));
@@ -35,21 +34,15 @@ export async function resolveRawEmail(row: RawMailRow): Promise<string> {
/** /**
* Resolve a single row: decompress raw_blob if present, strip raw_blob from result. * Resolve a single row: decompress raw_blob if present, strip raw_blob from result.
*/ */
export async function resolveRawEmailRow( export async function resolveRawEmailRow(row: RawMailRow): Promise<RawMailRow> {
row: RawMailRow,
enableMailStates = false,
): Promise<RawMailRow> {
const raw = await resolveRawEmail(row); const raw = await resolveRawEmail(row);
const { raw_blob: _, ...rest } = row; const { raw_blob: _, ...rest } = row;
return serializeMailState({ ...rest, raw }, enableMailStates); return { ...rest, raw };
} }
/** /**
* Batch resolve raw emails for list queries using Promise.all. * Batch resolve raw emails for list queries using Promise.all.
*/ */
export async function resolveRawEmailList( export async function resolveRawEmailList(rows: RawMailRow[]): Promise<RawMailRow[]> {
rows: RawMailRow[], return Promise.all(rows.map(row => resolveRawEmailRow(row)));
enableMailStates = false,
): Promise<RawMailRow[]> {
return Promise.all(rows.map(row => resolveRawEmailRow(row, enableMailStates)));
} }
+3 -2
View File
@@ -9,6 +9,7 @@ import {
getMailStateQuery, getMailStateQuery,
getMailStateOptions, getMailStateOptions,
applyMailStateUpdate, applyMailStateUpdate,
serializeMailState,
} from '../mail_flags'; } from '../mail_flags';
const listMails = async (c: Context<HonoCustomType>) => { const listMails = async (c: Context<HonoCustomType>) => {
@@ -45,8 +46,8 @@ const getMail = async (c: Context<HonoCustomType>) => {
`SELECT * FROM raw_mails where id = ? and address = ?` `SELECT * FROM raw_mails where id = ? and address = ?`
).bind(mail_id, address).first(); ).bind(mail_id, address).first();
if (!result) return c.json(null); if (!result) return c.json(null);
return c.json(await resolveRawEmailRow( return c.json(serializeMailState(
result, await resolveRawEmailRow(result),
getBooleanValue(c.env.ENABLE_MAIL_FLAGS), getBooleanValue(c.env.ENABLE_MAIL_FLAGS),
)); ));
}; };
+3 -2
View File
@@ -3,6 +3,7 @@ import { Context } from 'hono'
import { commonParseMail, handleMailListQuery, updateAddressUpdatedAt } from '../common' import { commonParseMail, handleMailListQuery, updateAddressUpdatedAt } from '../common'
import { resolveRawEmailRow } from '../gzip' import { resolveRawEmailRow } from '../gzip'
import { getBooleanValue } from '../utils'; import { getBooleanValue } from '../utils';
import { serializeMailState } from '../mail_flags';
const toParsedMailRow = async (row: Record<string, unknown>): Promise<Record<string, unknown>> => { const toParsedMailRow = async (row: Record<string, unknown>): Promise<Record<string, unknown>> => {
const raw = typeof row.raw === 'string' ? row.raw : ''; const raw = typeof row.raw === 'string' ? row.raw : '';
@@ -46,8 +47,8 @@ const getParsedMail = async (c: Context<HonoCustomType>) => {
`SELECT * FROM raw_mails where id = ? and address = ?` `SELECT * FROM raw_mails where id = ? and address = ?`
).bind(mail_id, address).first(); ).bind(mail_id, address).first();
if (!row) return c.json(null); if (!row) return c.json(null);
const resolved = await resolveRawEmailRow( const resolved = serializeMailState(
row, await resolveRawEmailRow(row),
getBooleanValue(c.env.ENABLE_MAIL_FLAGS), getBooleanValue(c.env.ENABLE_MAIL_FLAGS),
); );
return c.json(await toParsedMailRow(resolved)); return c.json(await toParsedMailRow(resolved));
+3 -2
View File
@@ -6,6 +6,7 @@ import { checkCfTurnstile, checkIsAdmin, getBooleanValue } from "../utils";
import { resolveRawEmailRow } from "../gzip"; import { resolveRawEmailRow } from "../gzip";
import { TelegramSettings } from "./settings"; import { TelegramSettings } from "./settings";
import i18n from "../i18n"; import i18n from "../i18n";
import { serializeMailState } from "../mail_flags";
const encoder = new TextEncoder(); const encoder = new TextEncoder();
const TG_AUTH_TIMEOUT = 300; const TG_AUTH_TIMEOUT = 300;
@@ -145,7 +146,7 @@ async function getMail(c: Context<HonoCustomType>): Promise<Response> {
if (!result) { if (!result) {
return c.text("Mail not found", 404); return c.text("Mail not found", 404);
} }
return c.json(await resolveRawEmailRow(result)); return c.json(serializeMailState(await resolveRawEmailRow(result), false));
} }
const userId = await checkTelegramAuth(c, initData); const userId = await checkTelegramAuth(c, initData);
const jwtList = await c.env.KV.get<string[]>(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, 'json') || []; const jwtList = await c.env.KV.get<string[]>(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, 'json') || [];
@@ -168,7 +169,7 @@ async function getMail(c: Context<HonoCustomType>): Promise<Response> {
return c.text(msgs.TgNoPermissionViewMailMsg, 403); return c.text(msgs.TgNoPermissionViewMailMsg, 403);
} }
} }
return c.json(await resolveRawEmailRow(result)); return c.json(serializeMailState(await resolveRawEmailRow(result), false));
} }
catch (e) { catch (e) {
return c.text((e as Error).message, 400); return c.text((e as Error).message, 400);