import { PrismaClient, RepoIndexingJobType } from '@sourcebot/db'; import { createLogger } from '@sourcebot/shared'; import express, { Request, Response } from 'express'; import 'express-async-errors'; import * as http from "http"; import z from 'zod'; import { ConnectionManager } from './connectionManager.js'; import { PromClient } from './promClient.js'; import { RepoIndexManager } from './repoIndexManager.js'; const logger = createLogger('api'); const PORT = 3060; export class Api { private server: http.Server; constructor( promClient: PromClient, private prisma: PrismaClient, private connectionManager: ConnectionManager, private repoIndexManager: RepoIndexManager, ) { const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Prometheus metrics endpoint app.use('/metrics', async (_req: Request, res: Response) => { res.set('Content-Type', promClient.registry.contentType); const metrics = await promClient.registry.metrics(); res.end(metrics); }); app.post('/api/sync-connection', this.syncConnection.bind(this)); app.post('/api/index-repo', this.indexRepo.bind(this)); this.server = app.listen(PORT, () => { logger.info(`API server is running on port ${PORT}`); }); } private async syncConnection(req: Request, res: Response) { const schema = z.object({ connectionId: z.number(), }).strict(); const parsed = schema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ error: parsed.error.message }); return; } const { connectionId } = parsed.data; const connection = await this.prisma.connection.findUnique({ where: { id: connectionId, } }); if (!connection) { res.status(404).json({ error: 'Connection not found' }); return; } const [jobId] = await this.connectionManager.createJobs([connection]); res.status(200).json({ jobId }); } private async indexRepo(req: Request, res: Response) { const schema = z.object({ repoId: z.number(), }).strict(); const parsed = schema.safeParse(req.body); if (!parsed.success) { res.status(400).json({ error: parsed.error.message }); return; } const { repoId } = parsed.data; const repo = await this.prisma.repo.findUnique({ where: { id: repoId }, }); if (!repo) { res.status(404).json({ error: 'Repo not found' }); return; } const [jobId] = await this.repoIndexManager.createJobs([repo], RepoIndexingJobType.INDEX); res.status(200).json({ jobId }); } public async dispose() { return new Promise((resolve, reject) => { this.server.close((err) => { if (err) reject(err); else resolve(undefined); }); }); } }