Wire up cron scheduling and the app entrypoint

This commit is contained in:
2026-08-12 21:23:08 +02:00
parent e712470013
commit 31de7f15db
2 changed files with 112 additions and 0 deletions

26
src/app/cron.ts Normal file
View File

@@ -0,0 +1,26 @@
import cron from 'node-cron';
import type { Logger } from '../core/logging/logger';
import type { AppConfig } from './config/AppConfig';
import type { RecommendationPipeline } from './pipelines/RecommendationPipeline';
export function scheduleCron(config: AppConfig, pipeline: RecommendationPipeline, logger: Logger): void {
if (!config.CRON_ENABLED) return;
cron.schedule(config.CRON_SCHEDULE, async () => {
logger.info('Cron: running recommendation pipeline');
try {
const recommendations = await pipeline.runAsync({
count: config.RECOMMEND_COUNT,
useLlm: config.LLM_ENABLED,
});
if (config.RADARR_ENABLED) {
await pipeline.autoRequest(recommendations);
}
logger.info({ count: recommendations.length }, 'Cron: pipeline finished');
} catch (err) {
logger.error({ err }, 'Cron: pipeline failed');
}
});
logger.info({ schedule: config.CRON_SCHEDULE }, 'Cron scheduled');
}

86
src/app/index.ts Normal file
View File

@@ -0,0 +1,86 @@
import 'dotenv/config';
import { loadAppConfig } from './config/AppConfig';
import { createLogger } from '../core/logging/logger';
import { LangfuseTracer } from '../core/langfuse/LangfuseTracer';
import { LangfusePromptGateway } from '../core/langfuse/LangfusePromptGateway';
import { PromptService } from '../core/langfuse/PromptService';
import { LlmClient } from '../core/llm/LlmClient';
import { buildServer } from '../core/server/buildServer';
import { JellyfinClient } from './clients/jellyfin/JellyfinClient';
import { TmdbClient } from './clients/tmdb/TmdbClient';
import { RadarrClient } from './clients/radarr/RadarrClient';
import { MovieRecommenderTool } from './tools/MovieRecommenderTool';
import { MovieSearchTool } from './tools/MovieSearchTool';
import { RecommendationPipeline } from './pipelines/RecommendationPipeline';
import { HealthController } from './controllers/HealthController';
import { RecommendationsController } from './controllers/RecommendationsController';
import { RequestController } from './controllers/RequestController';
import { WebhookController } from './controllers/WebhookController';
import { scheduleCron } from './cron';
async function main(): Promise<void> {
const config = loadAppConfig();
const logger = createLogger('sugarr');
const langfuseOpts = {
publicKey: config.LANGFUSE_PUBLIC_KEY,
secretKey: config.LANGFUSE_SECRET_KEY,
baseUrl: config.LANGFUSE_BASE_URL,
};
const tracer = new LangfuseTracer(langfuseOpts);
const promptGateway = new LangfusePromptGateway(langfuseOpts);
const promptService = new PromptService(promptGateway, config.LANGFUSE_PROMPT_CACHE_ENABLED);
const llmClient = new LlmClient({ baseUrl: config.LLM_BASE_URL, apiKey: config.LLM_API_KEY });
const jellyfin = new JellyfinClient(config.JELLYFIN_URL, config.JELLYFIN_API_KEY, config.JELLYFIN_USER_ID);
const tmdb = new TmdbClient(config.TMDB_API_KEY);
const radarr = config.RADARR_ENABLED
? new RadarrClient(config.RADARR_URL, config.RADARR_API_KEY, config.RADARR_QUALITY_PROFILE_ID, config.RADARR_ROOT_FOLDER)
: undefined;
const recommenderTool = new MovieRecommenderTool(promptService, llmClient);
const searchTool = new MovieSearchTool(promptService, llmClient);
const pipeline = new RecommendationPipeline(tracer, logger, {
jellyfin,
tmdb,
radarr,
tool: recommenderTool,
searchTool,
minRatedMovies: config.MIN_RATED_MOVIES,
maxRetry: config.RECOMMEND_MAX_RETRY,
});
logger.info(
{
jellyfin: config.JELLYFIN_URL,
llmEnabled: config.LLM_ENABLED,
radarrEnabled: config.RADARR_ENABLED,
cronEnabled: config.CRON_ENABLED,
apiEnabled: config.API_ENABLED,
},
'sugarr starting'
);
scheduleCron(config, pipeline, logger);
if (config.API_ENABLED) {
const app = buildServer({
secretKey: config.SECRET_KEY,
controllers: [
new HealthController(config),
new RecommendationsController(config, pipeline),
new RequestController(config, radarr),
new WebhookController(promptService),
],
});
await app.listen({ port: config.API_PORT, host: '0.0.0.0' });
logger.info({ port: config.API_PORT }, 'HTTP API listening');
}
}
main().catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});