wakatime serverless setup

This commit is contained in:
2026-02-06 11:28:36 +00:00
parent 9b4ce5e6c5
commit 416bebe8e3
7 changed files with 598 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
import type { IncomingMessage, ServerResponse } from "node:http";
import {
assertSetupSecret,
buildRedirectUri,
getClientIp,
getRequiredEnv,
isRateLimited,
isSetupEnabled,
json,
makeState,
} from "../_shared.ts";
export default async function handler(
req: IncomingMessage,
res: ServerResponse & {
status: (code: number) => { json: (payload: unknown) => void };
},
) {
if (req.method !== "GET") {
json(res, 405, { error: "Method not allowed" });
return;
}
if (!isSetupEnabled()) {
json(res, 403, { error: "Setup route disabled" });
return;
}
const ip = getClientIp(req);
if (isRateLimited(`oauth-start:${ip}`, 20)) {
json(res, 429, { error: "Too many requests" });
return;
}
const url = new URL(req.url ?? "", "https://localhost");
const setupSecret = url.searchParams.get("setup_secret") ?? undefined;
if (!assertSetupSecret(setupSecret)) {
json(res, 401, { error: "Unauthorized setup request" });
return;
}
try {
const clientId = getRequiredEnv("WAKATIME_CLIENT_ID");
const setupSecretValue = getRequiredEnv("WAKATIME_SETUP_SECRET");
const redirectUri = buildRedirectUri(req);
const state = makeState(setupSecretValue);
const authorizeUrl = new URL("https://wakatime.com/oauth/authorize");
authorizeUrl.searchParams.set("client_id", clientId);
authorizeUrl.searchParams.set("response_type", "code");
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
authorizeUrl.searchParams.set("scope", "read_stats email");
authorizeUrl.searchParams.set("state", state);
res.statusCode = 302;
res.setHeader("Location", authorizeUrl.toString());
res.end();
} catch (error) {
json(res, 500, {
error: "Failed to build OAuth authorize URL",
detail: error instanceof Error ? error.message : "unknown_error",
});
}
}