gohorsejobs/backoffice/src/plans/plans.controller.ts
NANDO9322 8f331c97d3 feat(backoffice): Implementa gestão de credenciais e novas abas administrativas
BACKEND:
- Implementa [CredentialsHandler](cci:2://file:///C:/Projetos/gohorsejobs/backend/internal/api/handlers/credentials_handler.go:9:0-11:1) e rotas /api/v1/system/credentials para gestão segura de chaves.
- Adiciona criptografia RSA no [CredentialsService](cci:2://file:///C:/Projetos/gohorsejobs/backend/internal/services/credentials_service.go:17:0-22:1) para proteger chaves de API (Stripe, Cloudflare, etc).
- Automatiza geração de pares de chaves RSA no .env via script.

FRONTEND:
- Refatora /dashboard/backoffice organizando em Abas: Dashboard, Planos, Stripe e Sistema.
- Implementa CRUD completo para gestão de Planos (criar, editar, remover).
- Adiciona visualização de status do Stripe e botão para limpar cache Cloudflare.
- Ajusta formatação de data nos logs para fuso horário America/Sao_Paulo.
- Atualiza pi.ts para suportar novos endpoints de planos e credenciais.
2026-01-09 17:18:51 -03:00

130 lines
2.9 KiB
TypeScript

import { Controller, Get, Post, Patch, Delete, Param, Body, NotFoundException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { PlansService, Plan } from './plans.service';
import { IsString, IsNumber, IsArray, IsBoolean, IsOptional } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
class CreatePlanDto {
@ApiPropertyOptional()
@IsString()
@IsOptional()
id?: string;
@ApiProperty()
@IsString()
name: string;
@ApiProperty()
@IsString()
description: string;
@ApiProperty()
@IsNumber()
monthlyPrice: number;
@ApiProperty()
@IsNumber()
yearlyPrice: number;
@ApiProperty()
@IsArray()
@IsString({ each: true })
features: string[];
@ApiPropertyOptional()
@IsBoolean()
@IsOptional()
popular?: boolean;
}
class UpdatePlanDto {
@ApiPropertyOptional()
@IsString()
@IsOptional()
name?: string;
@ApiPropertyOptional()
@IsString()
@IsOptional()
description?: string;
@ApiPropertyOptional()
@IsNumber()
@IsOptional()
monthlyPrice?: number;
@ApiPropertyOptional()
@IsNumber()
@IsOptional()
yearlyPrice?: number;
@ApiPropertyOptional()
@IsArray()
@IsString({ each: true })
@IsOptional()
features?: string[];
@ApiPropertyOptional()
@IsBoolean()
@IsOptional()
popular?: boolean;
}
@ApiTags('Plans')
@Controller('plans')
export class PlansController {
constructor(private readonly plansService: PlansService) { }
@Get()
@ApiOperation({ summary: 'Get all plans' })
getAllPlans() {
return this.plansService.getAllPlans();
}
@Get(':id')
@ApiOperation({ summary: 'Get plan by ID' })
getPlanById(@Param('id') id: string) {
const plan = this.plansService.getPlanById(id);
if (!plan) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return plan;
}
@Post()
@ApiBearerAuth()
@ApiOperation({ summary: 'Create a new plan' })
@ApiBody({ type: CreatePlanDto })
createPlan(@Body() body: CreatePlanDto) {
const plan: Plan = {
...body,
id: body.id || body.name.toLowerCase().replace(/\s+/g, '-'),
};
return this.plansService.createPlan(plan);
}
@Patch(':id')
@ApiBearerAuth()
@ApiOperation({ summary: 'Update a plan' })
@ApiBody({ type: UpdatePlanDto })
updatePlan(@Param('id') id: string, @Body() body: UpdatePlanDto) {
const plan = this.plansService.updatePlan(id, body);
if (!plan) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return plan;
}
@Delete(':id')
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete a plan' })
deletePlan(@Param('id') id: string) {
const deleted = this.plansService.deletePlan(id);
if (!deleted) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return { message: `Plan ${id} deleted successfully` };
}
}