created a service container and moved all possible stuff to it to make light index files with reusable container

This commit is contained in:
Boki 2025-06-22 19:01:16 -04:00
parent 9a5e87ef4a
commit eeb5d1aca2
7 changed files with 766 additions and 633 deletions

View file

@ -1,183 +1,78 @@
/**
* Stock Bot Web API with Dependency Injection
* REST API service using Awilix container for managing connections
* Stock Bot Web API
* Simplified entry point using ServiceApplication framework
*/
// Framework imports
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { initializeServiceConfig } from '@stock-bot/config';
// Library imports
import {
ServiceApplication,
createServiceContainerFromConfig,
initializeServices as initializeAwilixServices,
type ServiceContainer
} from '@stock-bot/di';
import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger';
import { Shutdown } from '@stock-bot/shutdown';
import { getLogger } from '@stock-bot/logger';
// Local imports
import { createRoutes } from './routes/create-routes';
import { setupServiceContainer } from './container-setup';
// Initialize configuration
const config = initializeServiceConfig();
console.log('Web API Service Configuration:', JSON.stringify(config, null, 2));
const serviceConfig = config.service;
if (config.log) {
setLoggerConfig({
logLevel: config.log.level,
logConsole: true,
logFile: false,
environment: config.environment,
hideObject: config.log.hideObject,
// Create service application
const app = new ServiceApplication(
config,
{
serviceName: 'web-api',
enableHandlers: false, // Web API doesn't use handlers
enableScheduledJobs: false, // Web API doesn't use scheduled jobs
corsConfig: {
origin: ['http://localhost:4200', 'http://localhost:3000', 'http://localhost:3002'],
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
},
serviceMetadata: {
version: '1.0.0',
description: 'Stock Bot REST API',
endpoints: {
health: '/health',
exchanges: '/api/exchanges',
},
},
},
{
// Custom lifecycle hooks
onContainerReady: (container) => {
// Setup service-specific configuration
const enhancedContainer = setupServiceContainer(config, container);
return enhancedContainer;
},
onStarted: (port) => {
const logger = getLogger('web-api');
logger.info('Web API service startup initiated with ServiceApplication framework');
},
}
);
// Container factory function
async function createContainer(config: any) {
const container = createServiceContainerFromConfig(config, {
enableQuestDB: false, // Web API doesn't need QuestDB
enableMongoDB: true,
enablePostgres: true,
enableCache: true,
enableQueue: false, // Web API doesn't need queue processing
enableBrowser: false, // Web API doesn't need browser
enableProxy: false, // Web API doesn't need proxy
});
await initializeAwilixServices(container);
return container;
}
// Create logger AFTER config is set
const logger = getLogger('web-api');
const PORT = serviceConfig.port;
let server: ReturnType<typeof Bun.serve> | null = null;
let container: ServiceContainer | null = null;
let app: Hono | null = null;
// Initialize shutdown manager
const shutdown = Shutdown.getInstance({ timeout: 15000 });
// Initialize services with DI pattern
async function initializeServices() {
logger.info('Initializing web API service with DI...');
try {
// Create Awilix container directly from AppConfig
logger.debug('Creating Awilix DI container...');
container = createServiceContainerFromConfig(config, {
enableQuestDB: false, // Web API doesn't need QuestDB
enableMongoDB: true,
enablePostgres: true,
enableCache: true,
enableQueue: false, // Web API doesn't need queue processing
enableBrowser: false, // Web API doesn't need browser
enableProxy: false, // Web API doesn't need proxy
});
await initializeAwilixServices(container);
logger.info('Awilix container created and initialized');
// Setup service-specific configuration
const serviceContainer = setupServiceContainer(config, container.resolve('serviceContainer'));
// Create app with routes
app = new Hono();
// Add CORS middleware
app.use(
'*',
cors({
origin: ['http://localhost:4200', 'http://localhost:3000', 'http://localhost:3002'],
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
})
);
// Basic API info endpoint
app.get('/', c => {
return c.json({
name: 'Stock Bot Web API',
version: '1.0.0',
status: 'running',
timestamp: new Date().toISOString(),
endpoints: {
health: '/health',
exchanges: '/api/exchanges',
},
});
});
// Create and mount routes using the service container
const routes = createRoutes(serviceContainer);
app.route('/', routes);
logger.info('All services initialized successfully');
} catch (error) {
console.error('DETAILED ERROR:', error);
logger.error('Failed to initialize services', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
details: JSON.stringify(error, null, 2)
});
throw error;
}
}
// Start server
async function startServer() {
await initializeServices();
if (!app) {
throw new Error('App not initialized');
}
server = Bun.serve({
port: PORT,
fetch: app.fetch,
development: config.environment === 'development',
});
logger.info(`Web API service started on port ${PORT}`);
}
// Register shutdown handlers with priorities
// Priority 1: HTTP Server (high priority)
shutdown.onShutdownHigh(async () => {
if (server) {
logger.info('Stopping HTTP server...');
try {
server.stop();
logger.info('HTTP server stopped');
} catch (error) {
logger.error('Error stopping HTTP server', { error });
}
}
}, 'HTTP Server');
// Priority 2: Services and connections (medium priority)
shutdown.onShutdownMedium(async () => {
logger.info('Disposing services and connections...');
try {
if (container) {
// Disconnect database clients
const mongoClient = container.resolve('mongoClient');
if (mongoClient?.disconnect) await mongoClient.disconnect();
const postgresClient = container.resolve('postgresClient');
if (postgresClient?.disconnect) await postgresClient.disconnect();
logger.info('All services disposed successfully');
}
} catch (error) {
logger.error('Error disposing services', { error });
}
}, 'Services');
// Priority 3: Logger shutdown (lowest priority - runs last)
shutdown.onShutdownLow(async () => {
try {
logger.info('Shutting down loggers...');
await shutdownLoggers();
// Don't log after shutdown
} catch {
// Silently ignore logger shutdown errors
}
}, 'Loggers');
// Start the service
startServer().catch(error => {
app.start(createContainer, createRoutes).catch(error => {
const logger = getLogger('web-api');
logger.fatal('Failed to start web API service', { error });
process.exit(1);
});
logger.info('Web API service startup initiated with DI pattern');
});