"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createInstallationOctokit = createInstallationOctokit; exports.handleMount = handleMount; const auth_app_1 = require("@octokit/auth-app"); const rest_1 = require("@octokit/rest"); const supabase_1 = require("./supabase"); /** * Generates a GitHub App Installation Access Token and returns an * authenticated Octokit instance scoped to that installation. */ async function createInstallationOctokit(installationId) { const appId = process.env.APP_ID; let privateKey = process.env.PRIVATE_KEY; if (!appId || !privateKey) { throw new Error('APP_ID and PRIVATE_KEY must be set in environment variables.'); } // Sanitize private key (same as index.ts) privateKey = privateKey.replace(/\\n/g, '\n').replace(/^"(.*)"$/, '$1'); const auth = (0, auth_app_1.createAppAuth)({ appId: Number(appId), privateKey }); // Request an installation access token const installationAuth = await auth({ type: 'installation', installationId }); const octokit = new rest_1.Octokit({ auth: installationAuth.token }); return { octokit, token: installationAuth.token, expiresAt: installationAuth.expiresAt || new Date(Date.now() + 3600000).toISOString() }; } /** * Lists all repositories accessible to the given installation. * Handles pagination automatically. */ async function listInstallationRepos(octokit) { const repos = []; let page = 1; const perPage = 100; while (true) { const response = await octokit.request('GET /installation/repositories', { per_page: perPage, page }); const fetchedRepos = response.data.repositories || []; for (const repo of fetchedRepos) { repos.push(repo.full_name); } // If we got fewer than perPage, we've reached the last page if (fetchedRepos.length < perPage) { break; } page++; } return repos; } /** * Updates the user record in Supabase with the installation ID and * overwrites selected_repos with the repos from the installation. */ async function syncUserInstallation(githubId, installationId, repos) { // UPSERT: Update if exists, otherwise this is an error // (user should already exist from the OAuth signup flow) const result = await supabase_1.db.query(`UPDATE users SET github_installation_id = $1, selected_repos = $2, updated_at = NOW() WHERE github_id = $3`, [installationId, repos, githubId]); if (result.rowCount === 0) { throw new Error(`No user found with github_id=${githubId}. User must sign up before mounting repositories.`); } } /** * Main mount handler — called by the Express route. */ async function handleMount(body) { const { installationId, githubId } = body; // 1. Validate if (!installationId || !githubId) { return { success: false, repos: [], error: 'Missing required fields: installationId and githubId' }; } if (typeof installationId !== 'number' || typeof githubId !== 'number') { return { success: false, repos: [], error: 'installationId and githubId must be numbers' }; } try { // 2. Generate Installation Access Token console.log(`[MOUNT] Generating installation token for installation=${installationId}, user=${githubId}`); const { octokit, expiresAt } = await createInstallationOctokit(installationId); // 3. List accessible repos const repos = await listInstallationRepos(octokit); console.log(`[MOUNT] Found ${repos.length} repos for installation ${installationId}: ${repos.join(', ')}`); // 4. Sync to database (overwrite strategy) await syncUserInstallation(githubId, installationId, repos); console.log(`[MOUNT] Successfully synced ${repos.length} repos for user ${githubId}`); return { success: true, repos, tokenExpiresAt: expiresAt }; } catch (err) { console.error(`[MOUNT] Error: ${err.message}`); return { success: false, repos: [], error: err.message || 'Internal server error' }; } } //# sourceMappingURL=mount.js.map