UNPKG

repoweaver

Version:

A GitHub App that skillfully weaves multiple templates together to create and update repositories with intelligent merge strategies

221 lines โ€ข 8.18 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const express_1 = __importDefault(require("express")); const cors_1 = __importDefault(require("cors")); const body_parser_1 = require("body-parser"); const database_1 = require("./database"); const webhook_handler_1 = require("./webhook-handler"); const auth_1 = require("./auth"); const github_client_1 = require("./github-client"); const github_bootstrapper_1 = require("./github-bootstrapper"); const path_1 = __importDefault(require("path")); const app = (0, express_1.default)(); const port = process.env.PORT || 3000; // Database setup const database = new database_1.Database(process.env.DATABASE_URL || './app.db'); // Authentication setup const authManager = new auth_1.AuthManager(process.env.GITHUB_CLIENT_ID, process.env.GITHUB_CLIENT_SECRET, database); // Webhook handler setup const webhookHandler = new webhook_handler_1.WebhookHandler(process.env.GITHUB_WEBHOOK_SECRET, process.env.GITHUB_APP_ID, process.env.GITHUB_PRIVATE_KEY, database); // Middleware app.use((0, cors_1.default)()); app.use((0, body_parser_1.json)()); // Serve static files app.use(express_1.default.static(path_1.default.join(__dirname, '../public'))); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Authentication routes app.get('/auth/login', (req, res) => { const state = req.query.state; const authUrl = authManager.getAuthorizationUrl(state); res.redirect(authUrl); }); app.get('/auth/callback', async (req, res) => { try { const { code, state } = req.query; if (!code) { return res.status(400).json({ error: 'Missing authorization code' }); } const user = await authManager.handleCallback(code, state); // In a real app, you'd set a session cookie or return a JWT res.json({ success: true, user: { id: user.id, login: user.login, name: user.name, avatar_url: user.avatarUrl, has_installation: !!user.installationId } }); } catch (error) { console.error('Auth callback error:', error); res.status(500).json({ error: 'Authentication failed' }); } }); app.post('/auth/logout', authManager.authenticateMiddleware.bind(authManager), async (req, res) => { if (req.user) { await authManager.logout(req.user.id); } res.json({ success: true }); }); // API routes (require authentication) app.use('/api', authManager.authenticateMiddleware.bind(authManager)); app.use('/api', authManager.requireInstallation.bind(authManager)); // Get user info app.get('/api/user', (req, res) => { res.json(req.user); }); // Get repositories app.get('/api/repositories', async (req, res) => { try { const repositories = await authManager.getInstallationRepositories(req.user.installationId); res.json(repositories); } catch (error) { console.error('Failed to get repositories:', error); res.status(500).json({ error: 'Failed to get repositories' }); } }); // Get repository configuration app.get('/api/repositories/:owner/:repo/config', async (req, res) => { try { const { owner, repo } = req.params; const repository = `${owner}/${repo}`; const config = await database.getRepositoryConfig(req.user.installationId, repository); if (!config) { return res.json({ repository, templates: [], mergeStrategy: 'merge', excludePatterns: [], autoUpdate: true }); } res.json(config); } catch (error) { console.error('Failed to get repository config:', error); res.status(500).json({ error: 'Failed to get repository configuration' }); } }); // Update repository configuration app.put('/api/repositories/:owner/:repo/config', async (req, res) => { try { const { owner, repo } = req.params; const repository = `${owner}/${repo}`; const { templates, mergeStrategy, excludePatterns, autoUpdate } = req.body; await database.saveRepositoryConfig({ installationId: req.user.installationId, repository, templates, mergeStrategy, excludePatterns, autoUpdate, createdAt: new Date(), updatedAt: new Date() }); res.json({ success: true }); } catch (error) { console.error('Failed to update repository config:', error); res.status(500).json({ error: 'Failed to update repository configuration' }); } }); // Bootstrap repository app.post('/api/repositories/:owner/:repo/bootstrap', async (req, res) => { try { const { owner, repo } = req.params; const { templates, mergeStrategy, excludePatterns, createRepository } = req.body; const client = new github_client_1.GitHubClient(process.env.GITHUB_APP_ID, process.env.GITHUB_PRIVATE_KEY, req.user.installationId); const bootstrapper = new github_bootstrapper_1.GitHubBootstrapper(client); const result = await bootstrapper.bootstrap({ targetOwner: owner, targetRepo: repo, templates, repositoryName: repo, mergeStrategy, excludePatterns, createRepository }); res.json(result); } catch (error) { console.error('Bootstrap failed:', error); res.status(500).json({ error: 'Bootstrap failed' }); } }); // Update repository app.post('/api/repositories/:owner/:repo/update', async (req, res) => { try { const { owner, repo } = req.params; const { templates, mergeStrategy, excludePatterns } = req.body; const client = new github_client_1.GitHubClient(process.env.GITHUB_APP_ID, process.env.GITHUB_PRIVATE_KEY, req.user.installationId); const bootstrapper = new github_bootstrapper_1.GitHubBootstrapper(client); const result = await bootstrapper.updateRepository({ targetOwner: owner, targetRepo: repo, templates, repositoryName: repo, mergeStrategy, excludePatterns }); res.json(result); } catch (error) { console.error('Update failed:', error); res.status(500).json({ error: 'Update failed' }); } }); // Get jobs app.get('/api/jobs', async (req, res) => { try { const jobs = await database.getQueuedJobs(50); res.json(jobs); } catch (error) { console.error('Failed to get jobs:', error); res.status(500).json({ error: 'Failed to get jobs' }); } }); // Webhook endpoint app.post('/webhooks/github', webhookHandler.handleWebhook.bind(webhookHandler)); // Serve the web interface app.get('*', (req, res) => { res.sendFile(path_1.default.join(__dirname, '../public/index.html')); }); // Initialize database and start server async function startServer() { try { await database.initialize(); console.log('Database initialized successfully'); app.listen(port, () => { console.log(`๐Ÿงต RepoWeaver GitHub App running on port ${port}`); console.log(`๐Ÿ“ Webhook endpoint: http://localhost:${port}/webhooks/github`); console.log(`๐Ÿ” OAuth callback: http://localhost:${port}/auth/callback`); }); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } // Graceful shutdown process.on('SIGTERM', async () => { console.log('Received SIGTERM, shutting down gracefully...'); await database.close(); process.exit(0); }); process.on('SIGINT', async () => { console.log('Received SIGINT, shutting down gracefully...'); await database.close(); process.exit(0); }); startServer(); //# sourceMappingURL=app.js.map