Skip to content

Commit d0905e3

Browse files
hubyrodclaude
andcommitted
Rename groups to streams, add group config, and add /github/skipper route
Routes are now named skipper_stream and skjs_stream to reflect they target streams. Introduces a groups config section with a skipper group, and a new /github/skipper route that references it. Also improves webhook signature validation logging with route name and signature details. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent ad67083 commit d0905e3

5 files changed

Lines changed: 65 additions & 18 deletions

File tree

config.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,22 @@ const config: SkiphooksConfig = {
77
slashwork: {
88
graphqlUrl: process.env.SLASHWORK_GRAPHQL_URL!,
99
},
10+
groups: {
11+
skipper: {
12+
id: "https://skiplabs.slashwork.com/stream/g_dUYLNrxW7GzSxQwCKfGGQL",
13+
authToken: process.env.SLASHWORK_AUTH_TOKEN_SKIPPER!,
14+
},
15+
},
1016
routes: {
1117
skipper: {
12-
groupId: "g_dUYLNrxW7GzSxQwCKfGGQL",
18+
group: "skipper",
19+
},
20+
skipper_stream: {
21+
streamId: "g_dUYLNrxW7GzSxQwCKfGGQL",
1322
authToken: process.env.SLASHWORK_AUTH_TOKEN_SKIPPER!,
1423
},
15-
skjs: {
16-
groupId: "g_dUYLNrxW7GzSxQwCKfGGQL",
24+
skjs_stream: {
25+
streamId: "g_dUYLNrxW7GzSxQwCKfGGQL",
1726
authToken: process.env.SLASHWORK_AUTH_TOKEN_SKJS!,
1827
},
1928
},

src/config.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
export type EventType = "pull_request" | "issues" | "issue_comment" | "push" | "release";
22

3-
export interface RouteConfig {
4-
groupId: string;
3+
export interface StreamRouteConfig {
4+
streamId: string;
5+
authToken: string;
6+
}
7+
8+
export interface GroupRouteConfig {
9+
group: string;
10+
}
11+
12+
export type RouteConfig = StreamRouteConfig | GroupRouteConfig;
13+
14+
export interface GroupConfig {
15+
id: string;
516
authToken: string;
617
}
718

@@ -12,6 +23,7 @@ export interface SkiphooksConfig {
1223
slashwork: {
1324
graphqlUrl: string;
1425
};
26+
groups?: Record<string, GroupConfig>;
1527
routes: Record<string, RouteConfig>;
1628
}
1729

@@ -31,12 +43,29 @@ export function loadConfig(): SkiphooksConfig {
3143
throw new Error("config: at least one route must be configured");
3244
}
3345

34-
for (const [name, route] of Object.entries(config.routes)) {
35-
if (!route?.groupId) {
36-
throw new Error(`config: routes.${name}.groupId is required`);
46+
if (config.groups) {
47+
for (const [name, group] of Object.entries(config.groups)) {
48+
if (!group?.id) {
49+
throw new Error(`config: groups.${name}.id is required`);
50+
}
51+
if (!group?.authToken) {
52+
throw new Error(`config: groups.${name}.authToken is required`);
53+
}
3754
}
38-
if (!route?.authToken) {
39-
throw new Error(`config: routes.${name}.authToken is required`);
55+
}
56+
57+
for (const [name, route] of Object.entries(config.routes)) {
58+
if ("group" in route) {
59+
if (!config.groups?.[route.group]) {
60+
throw new Error(`config: routes.${name}.group references unknown group "${route.group}"`);
61+
}
62+
} else {
63+
if (!route?.streamId) {
64+
throw new Error(`config: routes.${name}.streamId is required`);
65+
}
66+
if (!route?.authToken) {
67+
throw new Error(`config: routes.${name}.authToken is required`);
68+
}
4069
}
4170
}
4271

src/index.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,20 @@ import { releaseHandler } from "./handlers/release.ts";
1111
const config = loadConfig();
1212
const port = parseInt(process.env.PORT || "3000", 10);
1313

14-
function connectionForRoute(routeName: string): SlashworkConnection {
14+
function resolveRoute(routeName: string): { targetId: string; authToken: string } {
1515
const route = config.routes[routeName]!;
16+
if ("group" in route) {
17+
const group = config.groups![route.group]!;
18+
return { targetId: group.id, authToken: group.authToken };
19+
}
20+
return { targetId: route.streamId, authToken: route.authToken };
21+
}
22+
23+
function connectionForRoute(routeName: string): SlashworkConnection {
24+
const { authToken } = resolveRoute(routeName);
1625
return {
1726
graphqlUrl: config.slashwork.graphqlUrl,
18-
authToken: route.authToken,
27+
authToken,
1928
};
2029
}
2130

@@ -32,7 +41,7 @@ function log(level: string, message: string) {
3241
}
3342

3443
async function handleWebhook(req: Request, routeName: string): Promise<Response> {
35-
const route = config.routes[routeName]!;
44+
const { targetId } = resolveRoute(routeName);
3645
const connection = connectionForRoute(routeName);
3746
if (req.method !== "POST") {
3847
return new Response("Method not allowed", { status: 405 });
@@ -47,7 +56,7 @@ async function handleWebhook(req: Request, routeName: string): Promise<Response>
4756

4857
const signature = req.headers.get("x-hub-signature-256");
4958
if (!verifySignature(body, signature, config.github.webhookSecret)) {
50-
log("warn", "Invalid webhook signature");
59+
log("warn", `Invalid webhook signature for route "${routeName}" (signature: ${signature ? `"${signature.slice(0, 20)}..."` : "missing"}, body length: ${body.length})`);
5160
return new Response("Invalid signature", { status: 401 });
5261
}
5362

@@ -81,7 +90,7 @@ async function handleWebhook(req: Request, routeName: string): Promise<Response>
8190

8291
try {
8392
const { markdown } = handler.format(payload);
84-
await postToSlashwork(connection, route.groupId, markdown);
93+
await postToSlashwork(connection, targetId, markdown);
8594
log("info", `Posted ${eventType} event: ${payload.action ?? "n/a"}`);
8695
} catch (err) {
8796
log("error", `Failed to post to Slashwork: ${err}`);

src/slashwork.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export async function validateConnection(
4747

4848
export async function postToSlashwork(
4949
connection: SlashworkConnection,
50-
groupId: string,
50+
streamId: string,
5151
markdown: string,
5252
): Promise<void> {
5353
const response = await fetch(connection.graphqlUrl, {
@@ -59,7 +59,7 @@ export async function postToSlashwork(
5959
body: JSON.stringify({
6060
query: CREATE_POST_MUTATION,
6161
variables: {
62-
groupId,
62+
groupId: streamId,
6363
markdown,
6464
},
6565
}),

test-webhook.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ set -euo pipefail
33

44
GITHUB_WEBHOOK_SECRET=f637d855c345d8e2f50ebf51de5480d3fa99e8b3af636aa5824ffdcfa4e3a58c
55

6-
ROUTE="${1:-skipper}"
6+
ROUTE="${1:-skipper_stream}"
77
BASE_URL="${2:-https://app-e18aa250-0ebc-4a2d-ba9f-1383450de63c.cleverapps.io}"
88
URL="${BASE_URL}/github/${ROUTE}"
99
SECRET="${GITHUB_WEBHOOK_SECRET:?Set GITHUB_WEBHOOK_SECRET env var}"

0 commit comments

Comments
 (0)