container lifecycle management
This commit is contained in:
146
gateway/src/auth/authenticator.ts
Normal file
146
gateway/src/auth/authenticator.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import type { FastifyRequest, FastifyBaseLogger } from 'fastify';
|
||||
import { UserService } from '../db/user-service.js';
|
||||
import { ChannelType, type AuthContext } from '../types/user.js';
|
||||
import type { ContainerManager } from '../k8s/container-manager.js';
|
||||
|
||||
export interface AuthenticatorConfig {
|
||||
userService: UserService;
|
||||
containerManager: ContainerManager;
|
||||
logger: FastifyBaseLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-channel authenticator
|
||||
* Handles authentication for WebSocket, Telegram, and other channels
|
||||
*/
|
||||
export class Authenticator {
|
||||
private config: AuthenticatorConfig;
|
||||
|
||||
constructor(config: AuthenticatorConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate WebSocket connection via JWT token
|
||||
* Also ensures the user's container is running
|
||||
*/
|
||||
async authenticateWebSocket(
|
||||
request: FastifyRequest
|
||||
): Promise<AuthContext | null> {
|
||||
try {
|
||||
const token = this.extractBearerToken(request);
|
||||
if (!token) {
|
||||
this.config.logger.warn('No bearer token in WebSocket connection');
|
||||
return null;
|
||||
}
|
||||
|
||||
const userId = await this.config.userService.verifyWebToken(token);
|
||||
if (!userId) {
|
||||
this.config.logger.warn('Invalid JWT token');
|
||||
return null;
|
||||
}
|
||||
|
||||
const license = await this.config.userService.getUserLicense(userId);
|
||||
if (!license) {
|
||||
this.config.logger.warn({ userId }, 'User license not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure container is running (may take time if creating new container)
|
||||
this.config.logger.info({ userId }, 'Ensuring user container is running');
|
||||
const { mcpEndpoint, wasCreated } = await this.config.containerManager.ensureContainerRunning(
|
||||
userId,
|
||||
license
|
||||
);
|
||||
|
||||
this.config.logger.info(
|
||||
{ userId, mcpEndpoint, wasCreated },
|
||||
'Container is ready'
|
||||
);
|
||||
|
||||
// Update license with actual MCP endpoint
|
||||
license.mcpServerUrl = mcpEndpoint;
|
||||
|
||||
const sessionId = `ws_${userId}_${Date.now()}`;
|
||||
|
||||
return {
|
||||
userId,
|
||||
channelType: ChannelType.WEBSOCKET,
|
||||
channelUserId: userId, // For WebSocket, same as userId
|
||||
sessionId,
|
||||
license,
|
||||
authenticatedAt: new Date(),
|
||||
};
|
||||
} catch (error) {
|
||||
this.config.logger.error({ error }, 'WebSocket authentication error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate Telegram webhook
|
||||
* Also ensures the user's container is running
|
||||
*/
|
||||
async authenticateTelegram(telegramUserId: string): Promise<AuthContext | null> {
|
||||
try {
|
||||
const userId = await this.config.userService.getUserIdFromChannel(
|
||||
'telegram',
|
||||
telegramUserId
|
||||
);
|
||||
|
||||
if (!userId) {
|
||||
this.config.logger.warn(
|
||||
{ telegramUserId },
|
||||
'Telegram user not linked to platform user'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const license = await this.config.userService.getUserLicense(userId);
|
||||
if (!license) {
|
||||
this.config.logger.warn({ userId }, 'User license not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure container is running
|
||||
this.config.logger.info({ userId }, 'Ensuring user container is running');
|
||||
const { mcpEndpoint, wasCreated } = await this.config.containerManager.ensureContainerRunning(
|
||||
userId,
|
||||
license
|
||||
);
|
||||
|
||||
this.config.logger.info(
|
||||
{ userId, mcpEndpoint, wasCreated },
|
||||
'Container is ready'
|
||||
);
|
||||
|
||||
// Update license with actual MCP endpoint
|
||||
license.mcpServerUrl = mcpEndpoint;
|
||||
|
||||
const sessionId = `tg_${telegramUserId}_${Date.now()}`;
|
||||
|
||||
return {
|
||||
userId,
|
||||
channelType: ChannelType.TELEGRAM,
|
||||
channelUserId: telegramUserId,
|
||||
sessionId,
|
||||
license,
|
||||
authenticatedAt: new Date(),
|
||||
};
|
||||
} catch (error) {
|
||||
this.config.logger.error({ error }, 'Telegram authentication error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract bearer token from request headers
|
||||
*/
|
||||
private extractBearerToken(request: FastifyRequest): string | null {
|
||||
const auth = request.headers.authorization;
|
||||
if (!auth || !auth.startsWith('Bearer ')) {
|
||||
return null;
|
||||
}
|
||||
return auth.substring(7);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user