simplify the server build

This commit is contained in:
Nate Kelley 2025-09-03 10:40:13 -06:00
parent 97e00e7b6b
commit ae19b51ecc
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
11 changed files with 15 additions and 178 deletions

View File

@ -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:",

View File

@ -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<InstallationTokenResponse> {
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
*/

View File

@ -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;
}

View File

@ -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';

View File

@ -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

View File

@ -33,7 +33,7 @@
"@buster/env-utils": "workspace:*",
"@buster/database": "workspace:*",
"@buster/server-shared": "workspace:*",
"octokit": "catalog:",
"octokit": "^5.0.3",
"zod": "catalog:"
},
"devDependencies": {

View File

@ -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'
);
});
});

View File

@ -11,6 +11,7 @@ export {
deleteInstallationToken,
isTokenExpired,
generateTokenVaultKey,
generateNewInstallationToken,
} from './services/token';
export {

View File

@ -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<InstallationTokenResponse> {

View File

@ -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:'

View File

@ -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