Skip to content

Commit 8e24f28

Browse files
committed
improve api route logging, secrets
1 parent 4ea156d commit 8e24f28

3 files changed

Lines changed: 124 additions & 42 deletions

File tree

src/routes/api/webhook/release.ts

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,92 @@ import { z } from "zod";
33
import { SendEmbed } from "@/lib/discord";
44

55
const envSchema = z.object({
6-
API_ROUTE_SECRET: z.string(),
6+
API_ROUTE_SECRET: z.string().min(1),
77
DISCORD_WEBHOOK_RELEASE: z.string().url(),
88
});
99

1010
export const Route = createFileRoute("/api/webhook/release")({
1111
server: {
1212
handlers: {
1313
POST: async ({ request }) => {
14-
const env = envSchema.parse(process.env);
14+
// Validate environment configuration
15+
const envResult = envSchema.safeParse(process.env);
16+
if (!envResult.success) {
17+
const missing = envResult.error.issues
18+
.map((i) => i.path.join("."))
19+
.join(", ");
20+
console.error(
21+
`[webhook/release] Environment configuration error: missing or invalid: ${missing}`,
22+
);
23+
return new Response("Internal server error", { status: 500 });
24+
}
25+
const env = envResult.data;
1526

1627
// Authorization
1728
const { searchParams } = new URL(request.url);
18-
if (searchParams.get("code") !== env.API_ROUTE_SECRET) {
19-
return new Response("You are not authorized to call this API", {
20-
status: 401,
21-
});
29+
const code = searchParams.get("code");
30+
31+
if (!code) {
32+
console.warn(
33+
"[webhook/release] Authorization failed: no code parameter provided",
34+
);
35+
return new Response("Unauthorized", { status: 401 });
36+
}
37+
38+
if (code !== env.API_ROUTE_SECRET) {
39+
console.warn(
40+
"[webhook/release] Authorization failed: invalid code parameter",
41+
);
42+
return new Response("Unauthorized", { status: 401 });
2243
}
2344

2445
// Parse body
2546
const reqBody = await request.text();
2647
if (reqBody === "") {
27-
return new Response("No body", { status: 400 });
48+
console.warn("[webhook/release] Bad request: empty body");
49+
return new Response("Bad request", { status: 400 });
2850
}
2951

30-
const data: {
52+
let data: {
3153
title?: string;
3254
description?: string;
3355
url?: string;
3456
timestamp?: Date;
3557
color?: number;
36-
} = JSON.parse(reqBody);
58+
};
3759

38-
console.log("Embed data:", data);
60+
try {
61+
data = JSON.parse(reqBody);
62+
} catch {
63+
console.warn("[webhook/release] Bad request: invalid JSON body");
64+
return new Response("Bad request", { status: 400 });
65+
}
66+
67+
console.log("[webhook/release] Processing embed:", data);
3968

4069
if (data.title) {
4170
data.title = `📦 | ${data.title}`;
4271
}
4372

4473
// Send embed
45-
await SendEmbed(env.DISCORD_WEBHOOK_RELEASE, {
46-
title: "🎁 | New Release",
47-
url: "https://github.com/pyclashbot/py-clash-bot/releases/latest",
48-
color: 0x03fc49,
49-
tagId: "1128136563201671221",
50-
...data,
51-
});
52-
53-
return new Response("Success", { status: 200 });
74+
try {
75+
await SendEmbed(env.DISCORD_WEBHOOK_RELEASE, {
76+
title: "🎁 | New Release",
77+
url: "https://github.com/pyclashbot/py-clash-bot/releases/latest",
78+
color: 0x03fc49,
79+
tagId: "1128136563201671221",
80+
...data,
81+
});
82+
} catch (err) {
83+
console.error(
84+
"[webhook/release] Failed to send Discord embed:",
85+
err,
86+
);
87+
return new Response("Internal server error", { status: 500 });
88+
}
89+
90+
console.log("[webhook/release] Embed sent successfully");
91+
return new Response("OK", { status: 200 });
5492
},
5593
},
5694
},

src/routes/api/webhook/release/prerelease.ts

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,92 @@ import { z } from "zod";
33
import { SendEmbed } from "@/lib/discord";
44

55
const envSchema = z.object({
6-
API_ROUTE_SECRET: z.string(),
6+
API_ROUTE_SECRET: z.string().min(1),
77
DISCORD_WEBHOOK_PRERELEASE: z.string().url(),
88
});
99

1010
export const Route = createFileRoute("/api/webhook/release/prerelease")({
1111
server: {
1212
handlers: {
1313
POST: async ({ request }) => {
14-
const env = envSchema.parse(process.env);
14+
// Validate environment configuration
15+
const envResult = envSchema.safeParse(process.env);
16+
if (!envResult.success) {
17+
const missing = envResult.error.issues
18+
.map((i) => i.path.join("."))
19+
.join(", ");
20+
console.error(
21+
`[webhook/prerelease] Environment configuration error: missing or invalid: ${missing}`,
22+
);
23+
return new Response("Internal server error", { status: 500 });
24+
}
25+
const env = envResult.data;
1526

1627
// Authorization
1728
const { searchParams } = new URL(request.url);
18-
if (searchParams.get("code") !== env.API_ROUTE_SECRET) {
19-
return new Response("You are not authorized to call this API", {
20-
status: 401,
21-
});
29+
const code = searchParams.get("code");
30+
31+
if (!code) {
32+
console.warn(
33+
"[webhook/prerelease] Authorization failed: no code parameter provided",
34+
);
35+
return new Response("Unauthorized", { status: 401 });
36+
}
37+
38+
if (code !== env.API_ROUTE_SECRET) {
39+
console.warn(
40+
"[webhook/prerelease] Authorization failed: invalid code parameter",
41+
);
42+
return new Response("Unauthorized", { status: 401 });
2243
}
2344

2445
// Parse body
2546
const reqBody = await request.text();
2647
if (reqBody === "") {
27-
return new Response("No body", { status: 400 });
48+
console.warn("[webhook/prerelease] Bad request: empty body");
49+
return new Response("Bad request", { status: 400 });
2850
}
2951

30-
const data: {
52+
let data: {
3153
title?: string;
3254
description?: string;
3355
url?: string;
3456
timestamp?: Date;
3557
color?: number;
36-
} = JSON.parse(reqBody);
58+
};
3759

38-
console.log("Embed data:", data);
60+
try {
61+
data = JSON.parse(reqBody);
62+
} catch {
63+
console.warn("[webhook/prerelease] Bad request: invalid JSON body");
64+
return new Response("Bad request", { status: 400 });
65+
}
66+
67+
console.log("[webhook/prerelease] Processing embed:", data);
3968

4069
if (data.title) {
4170
data.title = `📦 | ${data.title}`;
4271
}
4372

4473
// Send embed
45-
await SendEmbed(env.DISCORD_WEBHOOK_PRERELEASE, {
46-
title: "📦 | New Pre-Release",
47-
url: "https://github.com/pyclashbot/py-clash-bot/releases/latest",
48-
color: 0xfca503,
49-
tagId: "1128136612715450498",
50-
...data,
51-
});
52-
53-
return new Response("Success", { status: 200 });
74+
try {
75+
await SendEmbed(env.DISCORD_WEBHOOK_PRERELEASE, {
76+
title: "📦 | New Pre-Release",
77+
url: "https://github.com/pyclashbot/py-clash-bot/releases/latest",
78+
color: 0xfca503,
79+
tagId: "1128136612715450498",
80+
...data,
81+
});
82+
} catch (err) {
83+
console.error(
84+
"[webhook/prerelease] Failed to send Discord embed:",
85+
err,
86+
);
87+
return new Response("Internal server error", { status: 500 });
88+
}
89+
90+
console.log("[webhook/prerelease] Embed sent successfully");
91+
return new Response("OK", { status: 200 });
5492
},
5593
},
5694
},

sst.config.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ export default $config({
1919
const stage = $app.stage;
2020
const isProduction = stage === "production";
2121

22+
// Secrets
23+
const apiRouteSecret = new sst.Secret("ApiRouteSecret");
24+
const discordWebhookRelease = new sst.Secret("DiscordWebhookRelease");
25+
const discordWebhookPrerelease = new sst.Secret("DiscordWebhookPrerelease");
26+
const githubToken = new sst.Secret("GithubToken");
27+
2228
const baseDomain = isProduction
2329
? "pyclashbot.app"
2430
: `${stage}.pyclashbot.app`;
@@ -40,10 +46,10 @@ export default $config({
4046
instance: router,
4147
},
4248
environment: {
43-
API_ROUTE_SECRET: process.env.API_ROUTE_SECRET || "",
44-
DISCORD_WEBHOOK_RELEASE: process.env.DISCORD_WEBHOOK_RELEASE || "",
45-
DISCORD_WEBHOOK_PRERELEASE:
46-
process.env.DISCORD_WEBHOOK_PRERELEASE || "",
49+
API_ROUTE_SECRET: apiRouteSecret.value,
50+
DISCORD_WEBHOOK_RELEASE: discordWebhookRelease.value,
51+
DISCORD_WEBHOOK_PRERELEASE: discordWebhookPrerelease.value,
52+
GITHUB_TOKEN: githubToken.value,
4753
ANALYTICS_ID: process.env.ANALYTICS_ID || "",
4854
},
4955
});

0 commit comments

Comments
 (0)