mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 08:59:40 +02:00
- Add Swagger/OpenAPI documentation at /docs endpoint - Add admin module for system-wide API key management - Add scheduler for monthly credit reset and usage cleanup - Add Docker Compose entry for Mac Mini deployment - Document all endpoints with descriptions and examples
160 lines
5.1 KiB
TypeScript
160 lines
5.1 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Patch,
|
|
Delete,
|
|
Body,
|
|
Param,
|
|
Query,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import {
|
|
ApiTags,
|
|
ApiBearerAuth,
|
|
ApiOperation,
|
|
ApiResponse,
|
|
ApiParam,
|
|
ApiQuery,
|
|
} from '@nestjs/swagger';
|
|
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';
|
|
|
|
@ApiTags('API Keys')
|
|
@ApiBearerAuth('jwt')
|
|
@Controller('api-keys')
|
|
@UseGuards(JwtAuthGuard)
|
|
export class ApiKeysController {
|
|
constructor(
|
|
private readonly apiKeyService: ApiKeysService,
|
|
private readonly usageService: UsageService
|
|
) {}
|
|
|
|
@Post()
|
|
@ApiOperation({
|
|
summary: 'Create API key',
|
|
description: 'Creates a new API key. The full key is only returned once - save it securely.',
|
|
})
|
|
@ApiResponse({ status: 201, description: 'API key created successfully' })
|
|
@ApiResponse({ status: 401, description: 'Unauthorized - invalid or missing JWT' })
|
|
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()
|
|
@ApiOperation({
|
|
summary: 'List API keys',
|
|
description: 'Returns all API keys for the authenticated user (without full key values)',
|
|
})
|
|
@ApiResponse({ status: 200, description: 'List of API keys' })
|
|
async list(@CurrentUser() user: CurrentUserData) {
|
|
const keys = await this.apiKeyService.listByUser(user.userId);
|
|
return { apiKeys: keys };
|
|
}
|
|
|
|
@Get(':id')
|
|
@ApiOperation({
|
|
summary: 'Get API key details',
|
|
description: 'Returns details for a specific API key',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiResponse({ status: 200, description: 'API key details' })
|
|
@ApiResponse({ status: 404, description: 'API key not found' })
|
|
async get(@CurrentUser() user: CurrentUserData, @Param('id') id: string) {
|
|
const key = await this.apiKeyService.getByIdAndUser(id, user.userId);
|
|
return { apiKey: key };
|
|
}
|
|
|
|
@Patch(':id')
|
|
@ApiOperation({
|
|
summary: 'Update API key',
|
|
description: 'Updates name, description, allowed endpoints, IP whitelist, or active status',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiResponse({ status: 200, description: 'API key updated successfully' })
|
|
@ApiResponse({ status: 404, description: 'API key not found' })
|
|
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')
|
|
@ApiOperation({
|
|
summary: 'Delete API key',
|
|
description: 'Permanently deletes an API key. This action cannot be undone.',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiResponse({ status: 200, description: 'API key deleted successfully' })
|
|
@ApiResponse({ status: 404, description: 'API key not found' })
|
|
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')
|
|
@ApiOperation({
|
|
summary: 'Regenerate API key',
|
|
description:
|
|
'Generates a new key value for an existing API key. The old key immediately stops working.',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiResponse({ status: 200, description: 'New key generated successfully' })
|
|
@ApiResponse({ status: 404, description: 'API key not found' })
|
|
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')
|
|
@ApiOperation({
|
|
summary: 'Get daily usage',
|
|
description: 'Returns daily usage statistics for an API key',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiQuery({ name: 'days', required: false, description: 'Number of days (default: 30)' })
|
|
@ApiResponse({ status: 200, description: 'Daily usage statistics' })
|
|
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')
|
|
@ApiOperation({
|
|
summary: 'Get usage summary',
|
|
description: 'Returns aggregated usage summary for an API key',
|
|
})
|
|
@ApiParam({ name: 'id', description: 'API key ID (UUID)' })
|
|
@ApiResponse({ status: 200, description: '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 };
|
|
}
|
|
}
|