Skip to content

Commit bd01ef8

Browse files
hubyrodclaude
andcommitted
Support per-route Slashwork auth tokens and add SKJS route
Each Slashwork app has its own auth token. Move authToken from the global slashwork config into each RouteConfig so skipper and skjs can post with different application identities. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 87632d5 commit bd01ef8

3 files changed

Lines changed: 32 additions & 18 deletions

File tree

config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ const config: SkiphooksConfig = {
66
},
77
slashwork: {
88
graphqlUrl: process.env.SLASHWORK_GRAPHQL_URL!,
9-
authToken: process.env.SLASHWORK_AUTH_TOKEN!,
109
},
1110
routes: {
12-
"skipper": { groupId: "g_dUYLNrxW7GzSxQwCKfGGQL" },
11+
skipper: {
12+
groupId: "g_dUYLNrxW7GzSxQwCKfGGQL",
13+
authToken: process.env.SLASHWORK_AUTH_TOKEN_SKIPPER!,
14+
},
15+
skjs: {
16+
groupId: "g_dUYLNrxW7GzSxQwCKfGGQL",
17+
authToken: process.env.SLASHWORK_AUTH_TOKEN_SKJS!,
18+
},
1319
},
1420
};
1521

src/config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export type EventType = "pull_request" | "issues" | "issue_comment" | "push" | "
22

33
export interface RouteConfig {
44
groupId: string;
5+
authToken: string;
56
}
67

78
export interface SkiphooksConfig {
@@ -10,7 +11,6 @@ export interface SkiphooksConfig {
1011
};
1112
slashwork: {
1213
graphqlUrl: string;
13-
authToken: string;
1414
};
1515
routes: Record<string, RouteConfig>;
1616
}
@@ -27,9 +27,6 @@ export function loadConfig(): SkiphooksConfig {
2727
if (!config.slashwork?.graphqlUrl) {
2828
throw new Error("config: slashwork.graphqlUrl is required");
2929
}
30-
if (!config.slashwork?.authToken) {
31-
throw new Error("config: slashwork.authToken is required");
32-
}
3330
if (!config.routes || Object.keys(config.routes).length === 0) {
3431
throw new Error("config: at least one route must be configured");
3532
}
@@ -38,6 +35,9 @@ export function loadConfig(): SkiphooksConfig {
3835
if (!route?.groupId) {
3936
throw new Error(`config: routes.${name}.groupId is required`);
4037
}
38+
if (!route?.authToken) {
39+
throw new Error(`config: routes.${name}.authToken is required`);
40+
}
4141
}
4242

4343
return config;

src/index.ts

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

14-
const connection: SlashworkConnection = {
15-
graphqlUrl: config.slashwork.graphqlUrl,
16-
authToken: config.slashwork.authToken,
17-
};
14+
function connectionForRoute(routeName: string): SlashworkConnection {
15+
const route = config.routes[routeName]!;
16+
return {
17+
graphqlUrl: config.slashwork.graphqlUrl,
18+
authToken: route.authToken,
19+
};
20+
}
1821

1922
const handlers: Record<EventType, EventHandler> = {
2023
pull_request: pullRequestHandler,
@@ -28,7 +31,9 @@ function log(level: string, message: string) {
2831
console.log(`[${new Date().toISOString()}] [${level}] ${message}`);
2932
}
3033

31-
async function handleWebhook(req: Request, groupId: string): Promise<Response> {
34+
async function handleWebhook(req: Request, routeName: string): Promise<Response> {
35+
const route = config.routes[routeName]!;
36+
const connection = connectionForRoute(routeName);
3237
if (req.method !== "POST") {
3338
return new Response("Method not allowed", { status: 405 });
3439
}
@@ -76,7 +81,7 @@ async function handleWebhook(req: Request, groupId: string): Promise<Response> {
7681

7782
try {
7883
const { markdown } = handler.format(payload);
79-
await postToSlashwork(connection, groupId, markdown);
84+
await postToSlashwork(connection, route.groupId, markdown);
8085
log("info", `Posted ${eventType} event: ${payload.action ?? "n/a"}`);
8186
} catch (err) {
8287
log("error", `Failed to post to Slashwork: ${err}`);
@@ -102,7 +107,7 @@ Bun.serve({
102107
log("warn", `No route configured for: ${name}`);
103108
return new Response("Not found", { status: 404 });
104109
}
105-
return handleWebhook(req, route.groupId);
110+
return handleWebhook(req, name);
106111
}
107112

108113
return new Response("Not found", { status: 404 });
@@ -113,9 +118,12 @@ const routeNames = Object.keys(config.routes);
113118
log("info", `Server running on port ${port}`);
114119
log("info", `Routes: ${routeNames.map(n => `/github/${n}`).join(", ")}`);
115120
log("info", `Slashwork URL: ${config.slashwork.graphqlUrl}`);
116-
log("info", `Auth token loaded: ${config.slashwork.authToken.slice(0, 8)}...`);
117121

118-
validateConnection(connection).then(
119-
() => log("info", "Slashwork auth validated"),
120-
(err) => log("error", `${err}`),
121-
);
122+
for (const name of routeNames) {
123+
const conn = connectionForRoute(name);
124+
log("info", `Route ${name}: token ${conn.authToken.slice(0, 8)}...`);
125+
validateConnection(conn).then(
126+
() => log("info", `Route ${name}: auth validated`),
127+
(err) => log("error", `Route ${name}: ${err}`),
128+
);
129+
}

0 commit comments

Comments
 (0)