mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
Some checks failed
Publish to ghcr / build (linux/amd64, blacksmith-4vcpu-ubuntu-2404) (push) Has been cancelled
Publish to ghcr / build (linux/arm64, blacksmith-8vcpu-ubuntu-2204-arm) (push) Has been cancelled
Update Roadmap Released / update (push) Has been cancelled
Publish to ghcr / merge (push) Has been cancelled
* generate protobuf types * stream poc over SSE * wip: make stream search api follow existing schema. Modify UI to support streaming * fix scrolling issue * Dockerfile * wip on lezer parser grammar for query language * add lezer tree -> grpc transformer * remove spammy log message * fix syntax highlighting by adding a module resolution for @lezer/common * further wip on query language * Add case sensitivity and regexp toggles * Improved type safety / cleanup for query lang * support search contexts * update Dockerfile with query langauge package * fix filter * Add skeletons to filter panel when search is streaming * add client side caching * improved cancelation handling * add isSearchExausted flag for flagging when a search captured all results * Add back posthog search_finished event * remove zoekt tenant enforcement * migrate blocking search over to grpc. Centralize everything in searchApi * branch handling * plumb file weburl * add repo_sets filter for repositories a user has access to * refactor a bunch of stuff + add support for passing in Query IR to search api * refactor * dev README * wip on better error handling * error handling for stream path * update mcp * changelog wip * type fix * style * Support rev:* wildcard * changelog * changelog nit * feedback * fix build * update docs and remove uneeded test file
150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import "./instrument.js";
|
|
|
|
import * as Sentry from "@sentry/node";
|
|
import { PrismaClient } from "@sourcebot/db";
|
|
import { createLogger, env, getConfigSettings, getDBConnectionString, hasEntitlement } from "@sourcebot/shared";
|
|
import 'express-async-errors';
|
|
import { existsSync } from 'fs';
|
|
import { mkdir } from 'fs/promises';
|
|
import { Redis } from 'ioredis';
|
|
import { Api } from "./api.js";
|
|
import { ConfigManager } from "./configManager.js";
|
|
import { ConnectionManager } from './connectionManager.js';
|
|
import { INDEX_CACHE_DIR, REPOS_CACHE_DIR, SHUTDOWN_SIGNALS } from './constants.js';
|
|
import { AccountPermissionSyncer } from "./ee/accountPermissionSyncer.js";
|
|
import { GithubAppManager } from "./ee/githubAppManager.js";
|
|
import { RepoPermissionSyncer } from './ee/repoPermissionSyncer.js';
|
|
import { shutdownPosthog } from "./posthog.js";
|
|
import { PromClient } from './promClient.js';
|
|
import { RepoIndexManager } from "./repoIndexManager.js";
|
|
|
|
|
|
const logger = createLogger('backend-entrypoint');
|
|
|
|
const reposPath = REPOS_CACHE_DIR;
|
|
const indexPath = INDEX_CACHE_DIR;
|
|
|
|
if (!existsSync(reposPath)) {
|
|
await mkdir(reposPath, { recursive: true });
|
|
}
|
|
if (!existsSync(indexPath)) {
|
|
await mkdir(indexPath, { recursive: true });
|
|
}
|
|
|
|
const prisma = new PrismaClient({
|
|
datasources: {
|
|
db: {
|
|
url: getDBConnectionString(),
|
|
},
|
|
},
|
|
});
|
|
|
|
const redis = new Redis(env.REDIS_URL, {
|
|
maxRetriesPerRequest: null
|
|
});
|
|
|
|
try {
|
|
await redis.ping();
|
|
logger.info('Connected to redis');
|
|
} catch (err: unknown) {
|
|
logger.error('Failed to connect to redis. Error:', err);
|
|
process.exit(1);
|
|
}
|
|
|
|
const promClient = new PromClient();
|
|
|
|
const settings = await getConfigSettings(env.CONFIG_PATH);
|
|
|
|
if (hasEntitlement('github-app')) {
|
|
await GithubAppManager.getInstance().init(prisma);
|
|
}
|
|
|
|
const connectionManager = new ConnectionManager(prisma, settings, redis, promClient);
|
|
const repoPermissionSyncer = new RepoPermissionSyncer(prisma, settings, redis);
|
|
const accountPermissionSyncer = new AccountPermissionSyncer(prisma, settings, redis);
|
|
const repoIndexManager = new RepoIndexManager(prisma, settings, redis, promClient);
|
|
const configManager = new ConfigManager(prisma, connectionManager, env.CONFIG_PATH);
|
|
|
|
connectionManager.startScheduler();
|
|
repoIndexManager.startScheduler();
|
|
|
|
if (env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && !hasEntitlement('permission-syncing')) {
|
|
logger.error('Permission syncing is not supported in current plan. Please contact team@sourcebot.dev for assistance.');
|
|
process.exit(1);
|
|
}
|
|
else if (env.EXPERIMENT_EE_PERMISSION_SYNC_ENABLED === 'true' && hasEntitlement('permission-syncing')) {
|
|
repoPermissionSyncer.startScheduler();
|
|
accountPermissionSyncer.startScheduler();
|
|
}
|
|
|
|
const api = new Api(
|
|
promClient,
|
|
prisma,
|
|
connectionManager,
|
|
repoIndexManager,
|
|
);
|
|
|
|
logger.info('Worker started.');
|
|
|
|
const listenToShutdownSignals = () => {
|
|
const signals = SHUTDOWN_SIGNALS;
|
|
|
|
let receivedSignal = false;
|
|
|
|
const cleanup = async (signal: string) => {
|
|
try {
|
|
if (receivedSignal) {
|
|
return;
|
|
}
|
|
receivedSignal = true;
|
|
|
|
logger.info(`Received ${signal}, cleaning up...`);
|
|
|
|
await repoIndexManager.dispose()
|
|
await connectionManager.dispose()
|
|
await repoPermissionSyncer.dispose()
|
|
await accountPermissionSyncer.dispose()
|
|
await configManager.dispose()
|
|
|
|
await prisma.$disconnect();
|
|
await redis.quit();
|
|
await api.dispose();
|
|
await shutdownPosthog();
|
|
|
|
logger.info('All workers shut down gracefully');
|
|
signals.forEach(sig => process.removeListener(sig, cleanup));
|
|
return 0;
|
|
} catch (error) {
|
|
Sentry.captureException(error);
|
|
logger.error('Error shutting down worker:', error);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
signals.forEach(signal => {
|
|
process.on(signal, (err) => {
|
|
cleanup(err).then(code => {
|
|
process.exit(code);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Register handlers for uncaught exceptions and unhandled rejections
|
|
process.on('uncaughtException', (err) => {
|
|
logger.error(`Uncaught exception: ${err.message}`);
|
|
cleanup('uncaughtException').then(() => {
|
|
process.exit(1);
|
|
});
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason, promise) => {
|
|
logger.error(`Unhandled rejection at: ${promise}, reason: ${reason}`);
|
|
cleanup('unhandledRejection').then(() => {
|
|
process.exit(1);
|
|
});
|
|
});
|
|
|
|
|
|
}
|
|
|
|
listenToShutdownSignals();
|