Skip to content

Commit d70f924

Browse files
authored
Merge pull request #23 from fga-eps-mds/docs/densidade-comentarios
docs: comenta a base de codigo da BFF (densidade de comentarios)
2 parents aa3fe1e + 822e856 commit d70f924

39 files changed

Lines changed: 668 additions & 5 deletions

src/config/app.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Montagem da aplicacao Express: registra middlewares globais, o health check, o
2+
// roteador da API e, por ultimo, o 404 e o tratador central de erros.
13
import cors from "cors";
24
import express from "express";
35
import helmet from "helmet";
@@ -13,11 +15,14 @@ import { middlewareTratamentoErros } from "@/shared/middlewares/tratamento-erros
1315

1416
const aplicacao = express();
1517

18+
// Cadeia de middlewares globais, na ordem em que rodam a cada requisicao:
19+
// log HTTP -> headers de seguranca (helmet) -> CORS -> parsing de JSON (limite 1mb).
1620
aplicacao.use(loggerHttp);
1721
aplicacao.use(helmet());
1822
aplicacao.use(cors(criarOpcoesCors(env.CORS_ORIGINS)));
1923
aplicacao.use(express.json({ limit: "1mb" }));
2024

25+
// Endpoint de health check usado por monitoramento/infra para saber se o BFF esta no ar.
2126
aplicacao.get("/health", (_request, response) => {
2227
return response.status(200).json({
2328
mensagem: MENSAGENS.bffEmExecucao,
@@ -28,8 +33,10 @@ aplicacao.get("/health", (_request, response) => {
2833
});
2934
});
3035

36+
// Todas as rotas da API ficam sob o prefixo /api/v1.
3137
aplicacao.use("/api/v1", apiRouter);
3238

39+
// Qualquer rota nao reconhecida ate aqui vira um 404 padronizado.
3340
aplicacao.use((_request, _response, next) => {
3441
next(
3542
new ErroAplicacao({
@@ -40,6 +47,7 @@ aplicacao.use((_request, _response, next) => {
4047
);
4148
});
4249

50+
// Tratador de erros sempre por ultimo, para capturar tudo o que veio antes.
4351
aplicacao.use(middlewareTratamentoErros);
4452

4553
export { aplicacao };

src/config/cors.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,40 @@ import { MENSAGENS } from "@/shared/constants/mensagens";
44
import { CodigoDeErro } from "@/shared/errors/codigos-de-erro";
55
import { ErroAplicacao } from "@/shared/errors/erro-aplicacao";
66

7+
/**
8+
* Converte a string de origens do .env em uma lista normalizada.
9+
*
10+
* Separa por virgula, remove espacos em volta e descarta entradas vazias.
11+
*
12+
* @param value String de origens separadas por virgula (ex.: "a,b,c").
13+
* @returns Lista de origens limpas.
14+
*/
715
export function parseCorsOrigins(value: string): string[] {
816
return value
917
.split(",")
1018
.map((origin) => origin.trim())
1119
.filter(Boolean);
1220
}
1321

22+
/**
23+
* Monta as opcoes do CORS a partir da lista de origens permitidas.
24+
*
25+
* Libera requisicoes sem origin (ex.: chamadas server-to-server e health checks)
26+
* e as de origens presentes na lista; as demais sao rejeitadas com 403.
27+
*
28+
* @param origensPermitidas Lista de origens autorizadas.
29+
* @returns Objeto de configuracao aceito pelo middleware cors.
30+
*/
1431
export function criarOpcoesCors(origensPermitidas: string[]): CorsOptions {
1532
return {
1633
origin(origin, callback) {
34+
// Sem origin (ex.: chamadas server-to-server/health) ou origin na lista: libera.
1735
if (!origin || origensPermitidas.includes(origin)) {
1836
callback(null, true);
1937
return;
2038
}
2139

40+
// Origin nao autorizada vira erro 403 tratado pelo middleware central.
2241
callback(
2342
new ErroAplicacao({
2443
codigoStatus: 403,

src/config/env.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,59 @@
1+
// Fonte unica de configuracao do BFF: le o ambiente, valida com Zod e exporta um
2+
// objeto "env" tipado e seguro para o resto da aplicacao consumir.
13
import dotenv from "dotenv";
24
import { z } from "zod";
35

6+
// Carrega o .env e valida todas as variaveis de ambiente com Zod na subida do app:
7+
// se algo obrigatorio faltar, o processo nao inicia (falha cedo, com erro claro).
48
dotenv.config();
59

610
const ambienteAtual = process.env.NODE_ENV ?? "development";
711
const ambienteTeste = ambienteAtual === "test";
812

13+
/**
14+
* Cria um validador de variavel obrigatoria (presente e nao vazia).
15+
*
16+
* @param nome Nome da variavel, usado na mensagem de erro.
17+
* @returns Schema Zod que exige uma string nao vazia.
18+
*/
919
const obrigatoria = (nome: string) => z.string().min(1, `${nome} is required.`);
1020

21+
/**
22+
* Cria um validador obrigatorio com fallback apenas em ambiente de teste.
23+
*
24+
* Em testes, aplica um valor padrao para nao exigir um .env completo; fora de
25+
* teste, mantem a variavel como obrigatoria.
26+
*
27+
* @param nome Nome da variavel, usado na mensagem de erro.
28+
* @param padrao Valor padrao aplicado somente em ambiente de teste.
29+
* @returns Schema Zod adequado ao ambiente atual.
30+
*/
1131
const obrigatoriaComDefaultDeTeste = (nome: string, padrao: string) =>
1232
ambienteTeste ? z.string().min(1).default(padrao) : obrigatoria(nome);
1333

34+
// Schema de todas as variaveis de ambiente do BFF, com defaults e validacao de tipo.
1435
const envSchema = z.object({
36+
// Ambiente de execucao; controla defaults de teste e nivel de log.
1537
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
38+
// Porta HTTP do BFF (coercao de string para numero positivo).
1639
PORT: z.coerce.number().int().positive().default(4000),
40+
// Verbosidade do logger, do mais critico (fatal) ao desligado (silent).
1741
LOG_LEVEL: z
1842
.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"])
1943
.default("info"),
44+
// Enderecos dos servicos internos para onde o BFF faz proxy.
2045
BACKEND_URL: obrigatoriaComDefaultDeTeste("BACKEND_URL", "http://localhost:3333"),
2146
QUIZ_SERVICE_URL: obrigatoriaComDefaultDeTeste("QUIZ_SERVICE_URL", "http://localhost:3334"),
47+
// IA ainda nao habilitada: aceita URL valida ou string vazia (desligada nesta release).
2248
AI_URL: z
2349
.string()
2450
.url()
2551
.or(z.literal(""))
2652
.default(""),
53+
// Segredos compartilhados: precisam bater com os dos servicos, senao da 401/403.
2754
INTERNAL_TOKEN: obrigatoriaComDefaultDeTeste("INTERNAL_TOKEN", "test-internal-token"),
2855
JWT_SECRET_KEY: obrigatoriaComDefaultDeTeste("JWT_SECRET_KEY", "test-secret"),
56+
// Lista de origens liberadas no CORS, informada separada por virgula no .env.
2957
CORS_ORIGINS: z
3058
.string()
3159
.default("")
@@ -35,11 +63,13 @@ const envSchema = z.object({
3563
.map((origem) => origem.trim())
3664
.filter(Boolean),
3765
),
66+
// Timeout das chamadas aos servicos internos (ms).
3867
REQUEST_TIMEOUT_MS: z.coerce.number().int().positive().default(15000),
3968
});
4069

4170
const parsedEnv = envSchema.safeParse(process.env);
4271

72+
// Configuracao invalida derruba o boot na hora, com a lista de campos problematicos.
4373
if (!parsedEnv.success) {
4474
throw new Error(
4575
`Invalid environment variables: ${JSON.stringify(z.flattenError(parsedEnv.error).fieldErrors)}`,

src/config/logger.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
// Configuracao de logging do BFF com pino: um logger base para a aplicacao e um
2+
// logger HTTP que registra cada requisicao com nivel proporcional ao status.
13
import pino from "pino";
24
import pinoHttp from "pino-http";
35

46
import { env } from "@/config/env";
57

8+
// Logger base da aplicacao. Cada log sai com servico/ambiente fixos (base) e
9+
// timestamp em ISO, facilitando filtrar por servico na agregacao de logs.
610
export const logger = pino({
711
level: env.LOG_LEVEL,
812
timestamp: pino.stdTimeFunctions.isoTime,
@@ -12,8 +16,10 @@ export const logger = pino({
1216
},
1317
});
1418

19+
// Logger de requisicoes HTTP, derivado do logger base.
1520
export const loggerHttp = pinoHttp({
1621
logger,
22+
// Define a severidade do log pelo desfecho: 5xx/erro = error, 4xx = warn, resto = info.
1723
customLogLevel(_request, response, error) {
1824
if (error || response.statusCode >= 500) {
1925
return "error";
@@ -25,6 +31,7 @@ export const loggerHttp = pinoHttp({
2531

2632
return "info";
2733
},
34+
// Loga so o essencial de cada req/res para nao poluir nem vazar dados sensiveis.
2835
serializers: {
2936
req(request) {
3037
return {

src/modules/perfil-social/perfil-social.controller.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { ErroAplicacao } from "@/shared/errors/erro-aplicacao";
55

66
import type { PerfilSocialService } from "./perfil-social.service";
77

8+
// Parametro de rota: id do usuario cujo perfil social sera consultado.
89
type PerfilSocialParams = {
910
usuarioId: string;
1011
};
1112

13+
// Filtros e paginacao aceitos na listagem de amigos.
1214
type ListarAmigosQuery = {
1315
page?: number;
1416
limit?: number;
@@ -18,15 +20,31 @@ type ListarAmigosQuery = {
1820

1921
type RequisicaoAutenticada = Pick<Request, "usuario" | "headers">;
2022

23+
/**
24+
* Controller HTTP do perfil social.
25+
*
26+
* Valida os dados da requisicao, delega ao PerfilSocialService e devolve a resposta
27+
* em JSON, encaminhando erros ao middleware central via next.
28+
*/
2129
export class PerfilSocialController {
2230
constructor(private readonly perfilSocialService: PerfilSocialService) {}
2331

32+
/**
33+
* GET da lista de amigos do usuario logado.
34+
*
35+
* Aceita paginacao e filtros por nome/nickname na query string.
36+
*
37+
* @param request Requisicao Express com os filtros de listagem.
38+
* @param response Resposta Express.
39+
* @param next Encaminha erros ao middleware central.
40+
*/
2441
listarAmigos = async (
2542
request: Request<unknown, unknown, unknown, ListarAmigosQuery>,
2643
response: Response,
2744
next: NextFunction,
2845
) => {
2946
try {
47+
// Resolve usuario e token e repassa os filtros (request.query) ao service.
3048
const usuario = this.obterUsuario(request);
3149
const authorization = this.obterAuthorization(request);
3250
const resultado = await this.perfilSocialService.listarAmigosSociais(
@@ -37,16 +55,25 @@ export class PerfilSocialController {
3755

3856
return response.status(200).json(resultado);
3957
} catch (error) {
58+
// Erros (inclusive 401 de validacao) vao para o middleware central.
4059
return next(error);
4160
}
4261
};
4362

63+
/**
64+
* GET do perfil social de um amigo especifico. O usuarioId vem na rota.
65+
*
66+
* @param request Requisicao Express tipada com o parametro usuarioId.
67+
* @param response Resposta Express.
68+
* @param next Encaminha erros ao middleware central.
69+
*/
4470
buscarPerfil = async (
4571
request: Request<PerfilSocialParams>,
4672
response: Response,
4773
next: NextFunction,
4874
) => {
4975
try {
76+
// O usuarioId da rota ja foi validado pela camada de rota antes de chegar aqui.
5077
const usuario = this.obterUsuario(request);
5178
const authorization = this.obterAuthorization(request);
5279
const perfil = await this.perfilSocialService.buscarPerfilSocial(
@@ -55,15 +82,24 @@ export class PerfilSocialController {
5582
request.params.usuarioId,
5683
);
5784

85+
// Envolve o perfil no envelope padrao (mensagem + dados) da API.
5886
return response.status(200).json({
5987
mensagem: "Perfil social encontrado.",
6088
dados: perfil,
6189
});
6290
} catch (error) {
91+
// O 404 de "nao e amigo" tambem cai aqui e e formatado pelo middleware central.
6392
return next(error);
6493
}
6594
};
6695

96+
/**
97+
* Exige que o usuario ja tenha sido autenticado pelo middleware.
98+
*
99+
* @param request Requisicao que deveria conter o usuario autenticado.
100+
* @returns O usuario autenticado.
101+
* @throws ErroAplicacao 401 quando nao ha usuario na requisicao.
102+
*/
67103
private obterUsuario(request: RequisicaoAutenticada) {
68104
if (!request.usuario?.id) {
69105
throw new ErroAplicacao({
@@ -76,6 +112,13 @@ export class PerfilSocialController {
76112
return request.usuario;
77113
}
78114

115+
/**
116+
* Recupera o header Authorization a ser repassado aos servicos internos.
117+
*
118+
* @param request Requisicao de onde o header e lido.
119+
* @returns O valor do header Authorization.
120+
* @throws ErroAplicacao 401 quando o header esta ausente.
121+
*/
79122
private obterAuthorization(request: RequisicaoAutenticada): string {
80123
const authorization = request.headers.authorization;
81124

src/modules/perfil-social/perfil-social.routes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,23 @@ import { middlewareAutenticacao } from "@/shared/middlewares/autenticacao.middle
66
import { PerfilSocialController } from "./perfil-social.controller";
77
import { PerfilSocialService } from "./perfil-social.service";
88

9+
// Liga o modulo de perfil social: instancia service e controller (compartilhados
10+
// tambem por amizade.routes) e registra a rota autenticada de perfil.
911
const perfilSocialService = new PerfilSocialService();
1012
const perfilSocialController = new PerfilSocialController(perfilSocialService);
1113

1214
const perfilSocialRouter = Router();
1315

16+
// Exige usuario autenticado para acessar qualquer perfil social.
1417
perfilSocialRouter.use(middlewareAutenticacao);
1518

19+
// Valida o usuarioId da rota antes de chamar o controller: se vier vazio/invalido,
20+
// corta com 400 e nem aciona o servico.
1621
perfilSocialRouter.get("/:usuarioId/social", (request, response, next) => {
22+
// Valida o id da rota com Zod: precisa ser string nao vazia (apos trim).
1723
const resultado = z.string().trim().min(1).safeParse(request.params.usuarioId);
1824

25+
// Id invalido para o fluxo aqui mesmo, sem chamar o controller/servicos.
1926
if (!resultado.success) {
2027
return response.status(400).json({
2128
erro: {
@@ -25,6 +32,7 @@ perfilSocialRouter.get("/:usuarioId/social", (request, response, next) => {
2532
});
2633
}
2734

35+
// Id valido: segue para o controller buscar o perfil social.
2836
return perfilSocialController.buscarPerfil(request, response, next);
2937
});
3038

0 commit comments

Comments
 (0)