From ae19b51eccd0493f62cbf56f7ec7c5ba91eb54bd Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 3 Sep 2025 10:40:13 -0600 Subject: [PATCH] simplify the server build --- apps/server/package.json | 1 - .../github/services/get-installation-token.ts | 69 ++------------ .../src/api/v2/github/services/github-app.ts | 95 ------------------- .../src/api/v2/github/services/index.ts | 8 +- .../services/verify-webhook-signature.ts | 2 +- packages/github/package.json | 2 +- .../github/src/client/app.test.ts | 4 +- packages/github/src/index.ts | 1 + packages/github/src/services/token.ts | 2 +- pnpm-lock.yaml | 8 +- pnpm-workspace.yaml | 1 - 11 files changed, 15 insertions(+), 178 deletions(-) delete mode 100644 apps/server/src/api/v2/github/services/github-app.ts rename apps/server/src/api/v2/github/services/github-app.test.ts => packages/github/src/client/app.test.ts (97%) diff --git a/apps/server/package.json b/apps/server/package.json index ebb172544..183861991 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -44,7 +44,6 @@ "hono": "catalog:", "hono-pino": "^0.10.2", "lodash-es": "catalog:", - "octokit": "catalog:", "pino": "^9.9.1", "pino-pretty": "^13.1.1", "tsup": "catalog:", diff --git a/apps/server/src/api/v2/github/services/get-installation-token.ts b/apps/server/src/api/v2/github/services/get-installation-token.ts index 9fff385f3..21459c1cd 100644 --- a/apps/server/src/api/v2/github/services/get-installation-token.ts +++ b/apps/server/src/api/v2/github/services/get-installation-token.ts @@ -1,12 +1,12 @@ -import { - getActiveGithubIntegration, - getGithubIntegrationByInstallationId, - updateGithubIntegration, -} from '@buster/database'; +import { getActiveGithubIntegration, getGithubIntegrationByInstallationId } from '@buster/database'; import type { InstallationTokenResponse } from '@buster/server-shared/github'; import { GitHubErrorCode } from '@buster/server-shared/github'; -import { createGitHubApp } from './github-app'; -import { isTokenExpired, retrieveInstallationToken, storeInstallationToken } from './token-storage'; + +import { + generateNewInstallationToken, + isTokenExpired, + retrieveInstallationToken, +} from '@buster/github'; /** * Get an installation token for a specific installation ID @@ -80,61 +80,6 @@ export async function getInstallationTokenByOrgId( return await getInstallationToken(integration.installationId); } -/** - * Generate a new installation token from GitHub - */ -async function generateNewInstallationToken( - installationId: string, - integrationId: string -): Promise { - try { - const app = createGitHubApp(); - - // Create installation access token - const { data } = await app.octokit.rest.apps.createInstallationAccessToken({ - installation_id: Number.parseInt(installationId, 10), - }); - - // Store the new token in vault - const vaultKey = await storeInstallationToken( - installationId, - data.token, - data.expires_at, - data.permissions, - data.repository_selection - ); - - // Update the integration with the new vault key - await updateGithubIntegration(integrationId, { - tokenVaultKey: vaultKey, - status: 'active', // Ensure status is active after successful token generation - }); - - console.info( - `Generated new token for installation ${installationId}, expires at ${data.expires_at}` - ); - - return { - token: data.token, - expires_at: data.expires_at, - permissions: data.permissions, - repository_selection: data.repository_selection, - }; - } catch (error) { - console.error(`Failed to generate token for installation ${installationId}:`, error); - - // If token generation fails, mark the integration as failed - await updateGithubIntegration(integrationId, { - status: 'suspended', - }); - - throw createGitHubError( - GitHubErrorCode.TOKEN_GENERATION_FAILED, - `Failed to generate token: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } -} - /** * Verify that an installation belongs to a specific organization */ diff --git a/apps/server/src/api/v2/github/services/github-app.ts b/apps/server/src/api/v2/github/services/github-app.ts deleted file mode 100644 index ded42fa53..000000000 --- a/apps/server/src/api/v2/github/services/github-app.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { GitHubOperationError } from '@buster/server-shared/github'; -import { GitHubErrorCode } from '@buster/server-shared/github'; -import { App } from 'octokit'; - -/** - * Get GitHub App credentials from environment variables - */ -export function getGitHubAppCredentials(): { - appId: number; - privateKey: string; - webhookSecret: string; -} { - const appId = process.env.GITHUB_APP_ID; - const privateKeyBase64 = process.env.GITHUB_APP_PRIVATE_KEY_BASE64; - const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET; - - if (!appId) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - 'GITHUB_APP_ID environment variable is not set' - ); - } - - if (!privateKeyBase64) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - 'GITHUB_APP_PRIVATE_KEY_BASE64 environment variable is not set' - ); - } - - if (!webhookSecret) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - 'GITHUB_WEBHOOK_SECRET environment variable is not set' - ); - } - - // Decode the private key from base64 - let privateKey: string; - try { - // Check if it's valid base64 first (allow whitespace which will be trimmed) - const trimmedBase64 = privateKeyBase64.trim(); - if (!/^[A-Za-z0-9+/]*={0,2}$/.test(trimmedBase64)) { - throw new Error('Invalid base64 format'); - } - privateKey = Buffer.from(trimmedBase64, 'base64').toString('utf-8'); - } catch (_error) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - 'Failed to decode GITHUB_APP_PRIVATE_KEY_BASE64: Invalid base64 encoding' - ); - } - - // Validate the private key format (support both RSA and PKCS#8 formats) - if (!privateKey.includes('BEGIN RSA PRIVATE KEY') && !privateKey.includes('BEGIN PRIVATE KEY')) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - 'Invalid GitHub App private key format. Expected PEM-encoded RSA private key or PKCS#8 private key' - ); - } - - return { - appId: Number.parseInt(appId, 10), - privateKey, - webhookSecret, - }; -} - -/** - * Create a configured GitHub App instance - */ -export function createGitHubApp(): App { - const { appId, privateKey } = getGitHubAppCredentials(); - - try { - return new App({ - appId, - privateKey, - }); - } catch (error) { - throw createGitHubError( - GitHubErrorCode.APP_CONFIGURATION_ERROR, - `Failed to create GitHub App: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } -} - -/** - * Create a GitHub operation error - */ -function createGitHubError(code: GitHubErrorCode, message: string): Error { - const error = new Error(message) as Error & GitHubOperationError; - error.code = code; - return error; -} diff --git a/apps/server/src/api/v2/github/services/index.ts b/apps/server/src/api/v2/github/services/index.ts index 21f09aaa3..f23241965 100644 --- a/apps/server/src/api/v2/github/services/index.ts +++ b/apps/server/src/api/v2/github/services/index.ts @@ -1,16 +1,10 @@ /** * GitHub Services * - * This module exports all GitHub-related service functions + * This module exports GitHub-related service functions * for handling GitHub App installations and tokens */ -// GitHub App configuration and creation -export { - createGitHubApp, - getGitHubAppCredentials, -} from './github-app'; - // Installation webhook handling export { handleInstallationCallback } from './handle-installation-callback'; diff --git a/apps/server/src/api/v2/github/services/verify-webhook-signature.ts b/apps/server/src/api/v2/github/services/verify-webhook-signature.ts index 0962d013b..dbc72a0d4 100644 --- a/apps/server/src/api/v2/github/services/verify-webhook-signature.ts +++ b/apps/server/src/api/v2/github/services/verify-webhook-signature.ts @@ -1,6 +1,6 @@ import { createHmac, timingSafeEqual } from 'node:crypto'; +import { getGitHubAppCredentials } from '@buster/github'; import { GitHubErrorCode } from '@buster/server-shared/github'; -import { getGitHubAppCredentials } from './github-app'; /** * Verify a GitHub webhook signature diff --git a/packages/github/package.json b/packages/github/package.json index cb8028328..655c95bba 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -33,7 +33,7 @@ "@buster/env-utils": "workspace:*", "@buster/database": "workspace:*", "@buster/server-shared": "workspace:*", - "octokit": "catalog:", + "octokit": "^5.0.3", "zod": "catalog:" }, "devDependencies": { diff --git a/apps/server/src/api/v2/github/services/github-app.test.ts b/packages/github/src/client/app.test.ts similarity index 97% rename from apps/server/src/api/v2/github/services/github-app.test.ts rename to packages/github/src/client/app.test.ts index 58dcf549d..d09a8de07 100644 --- a/apps/server/src/api/v2/github/services/github-app.test.ts +++ b/packages/github/src/client/app.test.ts @@ -1,6 +1,6 @@ import { App } from 'octokit'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { createGitHubApp, getGitHubAppCredentials } from './github-app'; +import { createGitHubApp, getGitHubAppCredentials } from './app'; // Mock the octokit module vi.mock('octokit', () => ({ @@ -98,7 +98,7 @@ describe('github-app', () => { // Act & Assert expect(() => getGitHubAppCredentials()).toThrow( - 'Invalid GitHub App private key format. Expected PEM-encoded RSA private key or PKCS#8 private key' + 'Invalid GitHub App private key format. Expected PEM-encoded private key' ); }); }); diff --git a/packages/github/src/index.ts b/packages/github/src/index.ts index 6f4503995..a7780799e 100644 --- a/packages/github/src/index.ts +++ b/packages/github/src/index.ts @@ -11,6 +11,7 @@ export { deleteInstallationToken, isTokenExpired, generateTokenVaultKey, + generateNewInstallationToken, } from './services/token'; export { diff --git a/packages/github/src/services/token.ts b/packages/github/src/services/token.ts index 6cfabd762..28c4867ec 100644 --- a/packages/github/src/services/token.ts +++ b/packages/github/src/services/token.ts @@ -229,7 +229,7 @@ export async function getInstallationTokenByOrgId( /** * Generate a new installation token from GitHub */ -async function generateNewInstallationToken( +export async function generateNewInstallationToken( installationId: string, integrationId: string ): Promise { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a32fa4c10..4e0636ae6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,9 +42,6 @@ catalogs: lodash-es: specifier: ^4.17.21 version: 4.17.21 - octokit: - specifier: ^5.0.3 - version: 5.0.3 pg: specifier: ^8.16.3 version: 8.16.3 @@ -181,9 +178,6 @@ importers: lodash-es: specifier: 'catalog:' version: 4.17.21 - octokit: - specifier: 'catalog:' - version: 5.0.3 pino: specifier: ^9.9.1 version: 9.9.1 @@ -1029,7 +1023,7 @@ importers: specifier: workspace:* version: link:../vitest-config octokit: - specifier: 'catalog:' + specifier: ^5.0.3 version: 5.0.3 zod: specifier: 'catalog:' diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1a4ef10b6..901d9b8d9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -20,7 +20,6 @@ catalog: drizzle-orm: ^0.44.5 lodash-es: ^4.17.21 hono: ^4.9.6 - octokit: ^5.0.3 pg: ^8.16.3 tsup: ^8.5.0 tsx: ^4.20.5