container lifecycle management

This commit is contained in:
2026-03-12 15:13:38 -04:00
parent e99ef5d2dd
commit b9cc397e05
61 changed files with 6880 additions and 31 deletions

154
gateway/src/main.ts Normal file
View File

@@ -0,0 +1,154 @@
import Fastify from 'fastify';
import websocket from '@fastify/websocket';
import cors from '@fastify/cors';
import { UserService } from './db/user-service.js';
import { Authenticator } from './auth/authenticator.js';
import { WebSocketHandler } from './channels/websocket-handler.js';
import { TelegramHandler } from './channels/telegram-handler.js';
import { KubernetesClient } from './k8s/client.js';
import { ContainerManager } from './k8s/container-manager.js';
const app = Fastify({
logger: {
level: process.env.LOG_LEVEL || 'info',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
});
// Configuration from environment
const config = {
port: parseInt(process.env.PORT || '3000'),
host: process.env.HOST || '0.0.0.0',
databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost/dexorder',
// LLM provider API keys
providerConfig: {
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
openaiApiKey: process.env.OPENAI_API_KEY,
googleApiKey: process.env.GOOGLE_API_KEY,
openrouterApiKey: process.env.OPENROUTER_API_KEY,
},
telegramBotToken: process.env.TELEGRAM_BOT_TOKEN || '',
// Kubernetes configuration
kubernetes: {
namespace: process.env.KUBERNETES_NAMESPACE || 'dexorder-agents',
inCluster: process.env.KUBERNETES_IN_CLUSTER === 'true',
context: process.env.KUBERNETES_CONTEXT,
agentImage: process.env.AGENT_IMAGE || 'ghcr.io/dexorder/agent:latest',
sidecarImage: process.env.SIDECAR_IMAGE || 'ghcr.io/dexorder/lifecycle-sidecar:latest',
storageClass: process.env.AGENT_STORAGE_CLASS || 'standard',
},
};
// Validate at least one LLM provider is configured
const hasAnyProvider = Object.values(config.providerConfig).some(key => !!key);
if (!hasAnyProvider) {
app.log.error('At least one LLM provider API key is required (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, or OPENROUTER_API_KEY)');
process.exit(1);
}
// Register plugins
await app.register(cors, {
origin: process.env.CORS_ORIGIN || '*',
});
await app.register(websocket, {
options: {
maxPayload: 1024 * 1024, // 1MB
},
});
// Initialize services
const userService = new UserService(config.databaseUrl);
// Initialize Kubernetes client and container manager
const k8sClient = new KubernetesClient({
namespace: config.kubernetes.namespace,
inCluster: config.kubernetes.inCluster,
context: config.kubernetes.context,
logger: app.log,
});
const containerManager = new ContainerManager({
k8sClient,
agentImage: config.kubernetes.agentImage,
sidecarImage: config.kubernetes.sidecarImage,
storageClass: config.kubernetes.storageClass,
namespace: config.kubernetes.namespace,
logger: app.log,
});
const authenticator = new Authenticator({
userService,
containerManager,
logger: app.log,
});
// Initialize channel handlers
const websocketHandler = new WebSocketHandler({
authenticator,
providerConfig: config.providerConfig,
});
const telegramHandler = new TelegramHandler({
authenticator,
providerConfig: config.providerConfig,
telegramBotToken: config.telegramBotToken,
});
// Register routes
websocketHandler.register(app);
telegramHandler.register(app);
// Health check
app.get('/health', async () => {
return {
status: 'ok',
timestamp: new Date().toISOString(),
};
});
// Graceful shutdown
const shutdown = async () => {
app.log.info('Shutting down gracefully...');
try {
await userService.close();
await app.close();
app.log.info('Shutdown complete');
process.exit(0);
} catch (error) {
app.log.error({ error }, 'Error during shutdown');
process.exit(1);
}
};
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
// Start server
try {
await app.listen({
port: config.port,
host: config.host,
});
app.log.info(
{
port: config.port,
host: config.host,
},
'Gateway server started'
);
} catch (error) {
app.log.error({ error }, 'Failed to start server');
process.exit(1);
}