import dotenv from "dotenv" import { createNodeMiddleware } from "octokit" import express from "express" import suggester from "code-suggester" import { type FileDiffContent } from "code-suggester/build/src/types" import { determineValidHunks, objectToMap } from "./utils" import { createPullRequestFromDiffContents } from "./create-pr-from-diffcontents" import { githubApp, webhookApiPath } from "./github-app" import { userNickname } from "./auth0-mgmt" import functions from "@codeflash-ai/common/token-functions" import * as console from "console" const userForAPIKey = functions.userForAPIKey if (process.env.NODE_ENV !== "production") { console.log("Using .env.local file to supply config environment variables") dotenv.config({ path: ".env.local" }) } const port = process.env.PORT || 3001 const appExpress = express() appExpress.use((req, res, next) => { console.log(`<<< Received ${req.method} request for ${req.url}`) console.log(`<<< Request headers: ${JSON.stringify(req.headers)}`) console.log(`<<< Request body: ${JSON.stringify(req.body)}`) console.log(`<<< Request params: ${JSON.stringify(req.params)}`) console.log(`<<< Request query: ${JSON.stringify(req.query)}`) next() }) // Mount the github webhook middleware onto the express application appExpress.use(createNodeMiddleware(githubApp.webhooks, { path: webhookApiPath })) appExpress.use(express.json()) appExpress.get("/", async (req, res) => { return res.status(200).send("OK") }) appExpress.get("/healthcheck", async (req, res) => { return res.status(200).send("OK") }) // Middleware to check for valid API key appExpress.use(async (req, res, next) => { const authHeader = req.headers.authorization if (!authHeader) { return res.status(401).send("Authorization header is missing") } const apiKey = authHeader.replace(/^Bearer\s+/, "") const userId = await userForAPIKey(apiKey) if (userId == null) { console.log(`Userid null for API key ${apiKey}. Returning 403`) return res.status(403).send("Invalid API key") } req.userId = userId next() }) appExpress.post("/cfapi/suggest-pr-changes", async (req, res) => { try { const { owner, repo, pullNumber, diffContents, prComment } = req.body const userId = req.userId if (!repo || !owner || !pullNumber) { return res.status(400).send("Missing required fields") } const nickname = await userNickname(userId) if (!nickname) { return res.status(401).send("Unauthorized") // Error getting user nickname } const repoInstallation = await githubApp.octokit.rest.apps.getRepoInstallation({ owner, repo, }) const installationId = repoInstallation.data.id const installationOctokit = await githubApp.getInstallationOctokit(installationId) try { // Check if the user is a collaborator on the repository const response = await installationOctokit.rest.repos.checkCollaborator({ owner, repo, username: nickname, }) // If the request is successful, the user is a collaborator and we can continue if (response.status == 204) { console.log(`${nickname} is a collaborator on ${owner}/${repo}`) } } catch (error) { if (error.status === 404) { return res.status(401).send("Unauthorized") // User is not a collaborator } // Handle other potential errors return res.status(500).send(`Error`) // Error checking collaborator status } // Suggest changes const diffContentsMap: Map = objectToMap(diffContents) const { validHunks, invalidHunks } = await determineValidHunks( installationOctokit.rest, { owner, repo }, pullNumber, 100, diffContentsMap, ) if (invalidHunks.size > 0) { // we can't suggest all of the hunks for this PR, because some of them are invalid (out of scope for this PR). // so instead, let's make a new branch, commit the changes, // and make a new PR from that branch onto the PR's branch const newPrData = await createPullRequestFromDiffContents( installationOctokit, owner, repo, pullNumber, diffContentsMap, ) // Add explanation comment const result = await installationOctokit.rest.issues.createComment({ owner, repo, issue_number: newPrData.data.number, body: prComment, }) // Respond with the new PR details res.json(newPrData.data.number) } else { // all suggestions are valid and can be made on this PR. // let's make the suggestions const reviewNumber = await suggester.reviewPullRequest( installationOctokit.rest, diffContentsMap, { repo, owner, pullNumber, pageSize: 100, }, ) // Add explanation comment const result = await installationOctokit.rest.issues.createComment({ owner, repo, issue_number: pullNumber, body: prComment, }) res.json(reviewNumber) } } catch (error) { res.status(500).send(`Error creating pull request: ${error.message}`) } }) // WORKS without express.json() vvvvv // const router = express.Router(); // // router.use(express.json()); // router.use(createNodeMiddleware(ghApp.webhooks, { path: "/api/github/webhooks" })); // // const app = express(); // app.use('/', router); // // app.listen(port) // also works vvvvv // const server = http.createServer(createNodeMiddleware(ghApp.webhooks)); // server.listen() // \/\/\/\/\//\/\ // const server = http.createServer(appExpress.); // Start the server appExpress.listen(port, () => { console.log( `Server is listening on ${port} for CF API requests. Github App webhook events mounted at ${webhookApiPath}`, ) console.log("Press Ctrl + C to quit.") })