Skip to content

Commit 614fcd6

Browse files
committed
fix: enhance error handling
1 parent 2eca866 commit 614fcd6

5 files changed

Lines changed: 142 additions & 75 deletions

File tree

src/database/d1.ts

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@ import type { Email, EmailSummary } from "@/schemas/emails/schema";
55
* Insert an email into the database
66
*/
77
export async function insertEmail(db: D1Database, emailData: Email) {
8-
return await db
9-
.prepare(
10-
`INSERT INTO emails (id, from_address, to_address, subject, received_at, html_content, text_content)
8+
try {
9+
const { success, error, meta } = await db
10+
.prepare(
11+
`INSERT INTO emails (id, from_address, to_address, subject, received_at, html_content, text_content)
1112
VALUES (?, ?, ?, ?, ?, ?, ?)`,
12-
)
13-
.bind(
14-
emailData.id,
15-
emailData.from_address,
16-
emailData.to_address,
17-
emailData.subject,
18-
emailData.received_at,
19-
emailData.html_content,
20-
emailData.text_content,
21-
)
22-
.run();
13+
)
14+
.bind(
15+
emailData.id,
16+
emailData.from_address,
17+
emailData.to_address,
18+
emailData.subject,
19+
emailData.received_at,
20+
emailData.html_content,
21+
emailData.text_content,
22+
)
23+
.run();
24+
return { success, error, meta };
25+
} catch (e: unknown) {
26+
const error = e instanceof Error ? e : new Error(String(e));
27+
return { success: false, error: error, meta: undefined };
28+
}
2329
}
2430

2531
/**
@@ -31,67 +37,97 @@ export async function getEmailsByRecipient(
3137
limit: number,
3238
offset: number,
3339
) {
34-
const { results } = await db
35-
.prepare(
36-
`SELECT id, from_address, to_address, subject, received_at
40+
try {
41+
const { results } = await db
42+
.prepare(
43+
`SELECT id, from_address, to_address, subject, received_at
3744
FROM emails
3845
WHERE to_address = ?
3946
ORDER BY received_at DESC
4047
LIMIT ? OFFSET ?`,
41-
)
42-
.bind(emailAddress, limit, offset)
43-
.all();
44-
return results as EmailSummary[];
48+
)
49+
.bind(emailAddress, limit, offset)
50+
.all();
51+
return { results: results as EmailSummary[], error: undefined };
52+
} catch (e: unknown) {
53+
const error = e instanceof Error ? e : new Error(String(e));
54+
return { results: [], error: error };
55+
}
4556
}
4657

4758
/**
4859
* Get an email by ID
4960
*/
5061
export async function getEmailById(db: D1Database, emailId: string) {
51-
const emailResult = await db.prepare("SELECT * FROM emails WHERE id = ?").bind(emailId).first();
52-
return emailResult as Email | null;
62+
try {
63+
const emailResult = await db.prepare("SELECT * FROM emails WHERE id = ?").bind(emailId).first();
64+
return { result: emailResult as Email | null, error: undefined };
65+
} catch (e: unknown) {
66+
const error = e instanceof Error ? e : new Error(String(e));
67+
return { result: null, error: error };
68+
}
5369
}
5470

5571
/**
5672
* Delete emails older than a specific timestamp
5773
*/
5874
export async function deleteOldEmails(db: D1Database, timestamp: number) {
59-
const { success, error, meta } = await db
60-
.prepare("DELETE FROM emails WHERE received_at < ?")
61-
.bind(timestamp)
62-
.run();
63-
return { success, error, meta };
75+
try {
76+
const { success, error, meta } = await db
77+
.prepare("DELETE FROM emails WHERE received_at < ?")
78+
.bind(timestamp)
79+
.run();
80+
return { success, error, meta };
81+
} catch (e: unknown) {
82+
const error = e instanceof Error ? e : new Error(String(e));
83+
return { success: false, error: error, meta: undefined };
84+
}
6485
}
6586

6687
/**
6788
* Delete emails by recipient email address
6889
*/
6990
export async function deleteEmailsByRecipient(db: D1Database, emailAddress: string) {
70-
const { success, error, meta } = await db
71-
.prepare("DELETE FROM emails WHERE to_address = ?")
72-
.bind(emailAddress)
73-
.run();
74-
return { success, error, meta };
91+
try {
92+
const { success, error, meta } = await db
93+
.prepare("DELETE FROM emails WHERE to_address = ?")
94+
.bind(emailAddress)
95+
.run();
96+
return { success, error, meta };
97+
} catch (e: unknown) {
98+
const error = e instanceof Error ? e : new Error(String(e));
99+
return { success: false, error: error, meta: undefined };
100+
}
75101
}
76102

77103
/**
78104
* Delete an email by ID
79105
*/
80106
export async function deleteEmailById(db: D1Database, emailId: string) {
81-
const { success, error, meta } = await db
82-
.prepare("DELETE FROM emails WHERE id = ?")
83-
.bind(emailId)
84-
.run();
85-
return { success, error, meta };
107+
try {
108+
const { success, error, meta } = await db
109+
.prepare("DELETE FROM emails WHERE id = ?")
110+
.bind(emailId)
111+
.run();
112+
return { success, error, meta };
113+
} catch (e: unknown) {
114+
const error = e instanceof Error ? e : new Error(String(e));
115+
return { success: false, error: error, meta: undefined };
116+
}
86117
}
87118

88119
/**
89120
* Count emails by recipient email address
90121
*/
91122
export async function countEmailsByRecipient(db: D1Database, emailAddress: string) {
92-
const result = await db
93-
.prepare("SELECT count(*) as count FROM emails WHERE to_address = ?")
94-
.bind(emailAddress)
95-
.first<{ count: number }>();
96-
return result?.count || 0;
123+
try {
124+
const result = await db
125+
.prepare("SELECT count(*) as count FROM emails WHERE to_address = ?")
126+
.bind(emailAddress)
127+
.first<{ count: number }>();
128+
return { count: result?.count || 0, error: undefined };
129+
} catch (e: unknown) {
130+
const error = e instanceof Error ? e : new Error(String(e));
131+
return { count: 0, error: error };
132+
}
97133
}

src/handlers/emailHandler.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,9 @@ export async function handleEmail(
3434
ctx.waitUntil(updateSenderStats(env.KV, message.from));
3535

3636
// Insert email
37-
await db.insertEmail(env.D1, emailData);
37+
const { success, error } = await db.insertEmail(env.D1, emailData);
38+
39+
if (!success) {
40+
throw new Error(`Failed to insert email: ${error}`);
41+
}
3842
}

src/routes/emailRoutes.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ emailRoutes.use("/emails/count/:emailAddress", validateDomain);
2323
// @ts-ignore - Ignoring OpenAPI type mismatch for utility functions
2424
emailRoutes.openapi(getEmailsRoute, async (c) => {
2525
const { emailAddress } = c.req.valid("param");
26-
2726
const { limit, offset } = c.req.valid("query");
28-
const results = await db.getEmailsByRecipient(c.env.D1, emailAddress, limit, offset);
2927

28+
const { results, error } = await db.getEmailsByRecipient(c.env.D1, emailAddress, limit, offset);
29+
30+
if (error) return c.json(ERR(error.message, "D1Error"), 500);
3031
return c.json(OK(results));
3132
});
3233

@@ -35,8 +36,9 @@ emailRoutes.openapi(getEmailsRoute, async (c) => {
3536
emailRoutes.openapi(getEmailsCountRoute, async (c) => {
3637
const { emailAddress } = c.req.valid("param");
3738

38-
const count = await db.countEmailsByRecipient(c.env.D1, emailAddress);
39+
const { count, error } = await db.countEmailsByRecipient(c.env.D1, emailAddress);
3940

41+
if (error) return c.json(ERR(error.message, "D1Error"), 500);
4042
return c.json(OK({ count }));
4143
});
4244

@@ -45,16 +47,21 @@ emailRoutes.openapi(getEmailsCountRoute, async (c) => {
4547
emailRoutes.openapi(deleteEmailsRoute, async (c) => {
4648
const { emailAddress } = c.req.valid("param");
4749

48-
const { meta } = await db.deleteEmailsByRecipient(c.env.D1, emailAddress);
49-
return c.json(OK({ message: "Emails deleted successfully", deleted_count: meta.changes }));
50+
const { meta, error } = await db.deleteEmailsByRecipient(c.env.D1, emailAddress);
51+
52+
if (error) return c.json(ERR(error.message, "D1Error"), 500);
53+
if (meta && meta.changes === 0)
54+
return c.json(ERR("No emails found for deletion", "NotFound"), 404);
55+
return c.json(OK({ message: "Emails deleted successfully", deleted_count: meta?.changes }));
5056
});
5157

5258
// GET /inbox/{emailId}
5359
// @ts-ignore - Ignoring OpenAPI type mismatch for utility functions
5460
emailRoutes.openapi(getEmailRoute, async (c) => {
5561
const { emailId } = c.req.valid("param");
56-
const result = await db.getEmailById(c.env.D1, emailId);
62+
const { result, error } = await db.getEmailById(c.env.D1, emailId);
5763

64+
if (error) return c.json(ERR(error.message, "D1Error"), 500);
5865
if (!result) return c.json(ERR("Email not found", "NotFound"), 404);
5966
return c.json(OK(result));
6067
});
@@ -63,9 +70,10 @@ emailRoutes.openapi(getEmailRoute, async (c) => {
6370
// @ts-ignore - Ignoring OpenAPI type mismatch for utility functions
6471
emailRoutes.openapi(deleteEmailRoute, async (c) => {
6572
const { emailId } = c.req.valid("param");
66-
const { meta } = await db.deleteEmailById(c.env.D1, emailId);
73+
const { meta, error } = await db.deleteEmailById(c.env.D1, emailId);
6774

68-
if (meta.changes === 0) return c.json(ERR("Email not found", "NotFound"), 404);
75+
if (error) return c.json(ERR(error.message, "D1Error"), 500);
76+
if (meta && meta.changes === 0) return c.json(ERR("Email not found", "NotFound"), 404);
6977
return c.json(OK({ message: "Email deleted successfully" }));
7078
});
7179

src/routes/healthRoutes.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { OpenAPIHono } from "@hono/zod-openapi";
2-
import { ERR, OK } from "@/utils/http";
2+
import type { ContentfulStatusCode } from "hono/utils/http-status";
3+
import { OK } from "@/utils/http";
34
import { logError } from "@/utils/logger";
45

56
const healthRoutes = new OpenAPIHono<{ Bindings: CloudflareBindings }>();
@@ -8,21 +9,34 @@ const healthRoutes = new OpenAPIHono<{ Bindings: CloudflareBindings }>();
89
// TODO: add it to the openapi docs
910
// GET /health
1011
healthRoutes.get("/health", async (c) => {
12+
let d1Status = "disconnected";
13+
let kvStatus = "disconnected";
14+
let overallStatus: ContentfulStatusCode = 200;
15+
1116
try {
1217
await c.env.D1.prepare("SELECT 1").run();
13-
await c.env.KV.list({ limit: 1 });
18+
d1Status = "connected";
19+
} catch (error) {
20+
logError("Health check D1 failed", error as Error);
21+
overallStatus = 503;
22+
}
1423

15-
return c.json(
16-
OK({
17-
worker: "connected",
18-
database: "connected",
19-
kv: "connected",
20-
}),
21-
);
24+
try {
25+
await c.env.KV.list({ limit: 1 });
26+
kvStatus = "connected";
2227
} catch (error) {
23-
logError("Health check failed", error as Error);
24-
return c.json(ERR((error as Error).message), 503);
28+
logError("Health check KV failed", error as Error);
29+
overallStatus = 503;
2530
}
31+
32+
return c.json(
33+
OK({
34+
worker: "connected",
35+
database: d1Status,
36+
kv: kvStatus,
37+
}),
38+
overallStatus,
39+
);
2640
});
2741

2842
export default healthRoutes;

src/utils/telegram.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
1-
import { logInfo } from "@/utils/logger";
1+
import { logError, logInfo } from "@/utils/logger";
22

33
/**
44
* Send message to Telegram
55
*/
66
export async function sendMessage(text: string, env: CloudflareBindings) {
77
if (!env.TELEGRAM_LOG_ENABLE || !env.TELEGRAM_BOT_TOKEN || !env.TELEGRAM_CHAT_ID) {
88
logInfo("Telegram logging is disabled.");
9+
return;
910
}
1011

1112
const url = `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`;
1213

13-
await fetch(url, {
14-
method: "POST",
15-
headers: {
16-
"Content-Type": "application/json",
17-
},
18-
body: JSON.stringify({
19-
chat_id: Number(env.TELEGRAM_CHAT_ID),
20-
text,
21-
parse_mode: "Markdown",
22-
}),
23-
});
14+
try {
15+
await fetch(url, {
16+
method: "POST",
17+
headers: {
18+
"Content-Type": "application/json",
19+
},
20+
body: JSON.stringify({
21+
chat_id: Number(env.TELEGRAM_CHAT_ID),
22+
text,
23+
parse_mode: "Markdown",
24+
}),
25+
});
26+
} catch (error) {
27+
logError("Failed to send Telegram message", error as Error);
28+
}
2429
}

0 commit comments

Comments
 (0)