feat: add mana-api-gateway for monetizing core services

Implement custom NestJS API Gateway for mana-search, mana-stt, and mana-tts:

- API Key management with CRUD operations and key regeneration
- Redis-based sliding window rate limiting
- Credit-based billing with tier support (free, pro, enterprise)
- Usage tracking with daily aggregates
- Proxy services to backend microservices
- Prometheus metrics endpoint
- JWT auth for management API, API key auth for public API

Database schema uses separate `api_gateway` schema in shared manacore DB.
This commit is contained in:
Till-JS 2026-01-29 17:30:21 +01:00
parent fbd315eac0
commit 6f1b2654f1
48 changed files with 2507 additions and 0 deletions

View file

@ -0,0 +1,97 @@
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
Query,
UseGuards,
} from '@nestjs/common';
import { JwtAuthGuard, CurrentUser, CurrentUserData } from '@manacore/shared-nestjs-auth';
import { ApiKeysService } from './api-keys.service';
import { CreateApiKeyDto, UpdateApiKeyDto } from './dto';
import { UsageService } from '../usage/usage.service';
@Controller('api-keys')
@UseGuards(JwtAuthGuard)
export class ApiKeysController {
constructor(
private readonly apiKeyService: ApiKeysService,
private readonly usageService: UsageService
) {}
@Post()
async create(@CurrentUser() user: CurrentUserData, @Body() dto: CreateApiKeyDto) {
const result = await this.apiKeyService.create(user.userId, dto);
return {
message: 'API key created successfully. Save your key - it will not be shown again.',
key: result.key,
apiKey: result.apiKey,
};
}
@Get()
async list(@CurrentUser() user: CurrentUserData) {
const keys = await this.apiKeyService.listByUser(user.userId);
return { apiKeys: keys };
}
@Get(':id')
async get(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
const key = await this.apiKeyService.getByIdAndUser(id, user.userId);
return { apiKey: key };
}
@Patch(':id')
async update(
@CurrentUser() user: CurrentUserData,
@Param('id') id: string,
@Body() dto: UpdateApiKeyDto
) {
const key = await this.apiKeyService.update(id, user.userId, dto);
return { apiKey: key };
}
@Delete(':id')
async delete(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
await this.apiKeyService.delete(id, user.userId);
return { message: 'API key deleted successfully' };
}
@Post(':id/regenerate')
async regenerate(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
const result = await this.apiKeyService.regenerate(id, user.userId);
return {
message: 'API key regenerated successfully. Save your new key - it will not be shown again.',
key: result.key,
apiKey: result.apiKey,
};
}
@Get(':id/usage')
async getUsage(
@CurrentUser() user: CurrentUserData,
@Param('id') id: string,
@Query('days') days?: string
) {
// Verify ownership
await this.apiKeyService.getByIdAndUser(id, user.userId);
const daysNum = parseInt(days || '30', 10);
const usage = await this.usageService.getDailyUsage(id, daysNum);
return { usage };
}
@Get(':id/usage/summary')
async getUsageSummary(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
// Verify ownership
await this.apiKeyService.getByIdAndUser(id, user.userId);
const summary = await this.usageService.getUsageSummary(id);
return { summary };
}
}