From 6ee80bff6dc79303408ab4e36f559dbd4ee3d3c9 Mon Sep 17 00:00:00 2001 From: carlosbastoslaet Date: Sat, 3 Jan 2026 10:50:34 -0300 Subject: [PATCH 1/2] fix lambda client --- .github/workflows/deploy.yml | 83 +++++++++ .github/workflows/destroy.yml | 49 ++++++ api_gateway.tf | 10 +- .../tech/buildrun/lambda/HandlerClient.java | 159 +++++++++++++----- lambda.tf | 6 +- vars.tf | 4 +- 6 files changed, 261 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/destroy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..2e915f9 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,83 @@ +name: Deploy Lambda to AWS + +on: + - push + - workflow_dispatch + +env: + TF_WORKSPACE: default + AWS_REGION: us-east-1 + +jobs: + deploy: + runs-on: ubuntu-latest + environment: Production + permissions: + id-token: write + contents: read + + steps: + # 1️⃣ Checkout do repositório + - uses: actions/checkout@v3 + + # 2️⃣ Setup Java 17 e cache Maven + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # 3️⃣ Build do Maven (fat JAR) + - name: Build fat JAR with Maven + run: | + cd app + mvn clean package -DskipTests + echo "📦 Conteúdo da pasta target:" + ls -la target/ + echo "➡️ Fat JAR gerado:" + ls -la target/lambda-identification-client.jar + + # 4️⃣ Configurar credenciais AWS + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: us-east-1 + + # 5️⃣ Setup Terraform + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + # 6️⃣ Terraform Init + - name: Terraform Init + run: terraform init + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + + # 7️⃣ Terraform Plan + - name: Terraform Plan + run: terraform plan + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + TF_VAR_db_user: ${{ secrets.DB_USER }} + TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} + TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }} + aws-region: us-east-1 + TF_VAR_lambda_jar_path: app/target/lambda-identification-client.jar + + # 8️⃣ Terraform Apply (apenas em main/master) + - name: Terraform Apply + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' + run: terraform apply -auto-approve + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + TF_VAR_db_user: ${{ secrets.DB_USER }} + TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} + TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }} + aws-region: us-east-1 + TF_VAR_lambda_jar_path: app/target/lambda-identification-client.jar diff --git a/.github/workflows/destroy.yml b/.github/workflows/destroy.yml new file mode 100644 index 0000000..9a62eec --- /dev/null +++ b/.github/workflows/destroy.yml @@ -0,0 +1,49 @@ +name: Destroy Lambda + +on: + workflow_dispatch: # Dispara manualmente + +env: + AWS_REGION: us-east-1 + +jobs: + destroy-lambda: + runs-on: ubuntu-latest + environment: Production + + steps: + # 1️⃣ Checkout do repositório + - uses: actions/checkout@v3 + + # 2️⃣ Configurar credenciais AWS + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ${{ env.AWS_REGION }} + + # 3️⃣ Verificar se a Lambda existe + - name: Check if Lambda exists + id: check_lambda + run: | + if aws lambda get-function --function-name lambda-identification-client --region $AWS_REGION > /dev/null 2>&1; then + echo "Lambda exists" + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "Lambda does not exist" + echo "exists=false" >> $GITHUB_OUTPUT + fi + + # 4️⃣ Deletar Lambda se existir + - name: Delete Lambda + if: steps.check_lambda.outputs.exists == 'true' + run: | + echo "Deleting Lambda..." + aws lambda delete-function --function-name lambda-identification-client --region $AWS_REGION + echo "✅ Lambda deleted successfully" + + # 5️⃣ Mensagem caso a Lambda não exista + - name: Lambda not found + if: steps.check_lambda.outputs.exists == 'false' + run: echo "⚠️ Lambda not found, nothing to delete" \ No newline at end of file diff --git a/api_gateway.tf b/api_gateway.tf index d7cc53a..239cbee 100644 --- a/api_gateway.tf +++ b/api_gateway.tf @@ -6,8 +6,14 @@ resource "aws_apigatewayv2_integration" "lambda_backend" { payload_format_version = "2.0" } -resource "aws_apigatewayv2_route" "auth_token_route" { +resource "aws_apigatewayv2_route" "clientes_post_route" { api_id = data.aws_apigatewayv2_api.tc_api.id - route_key = "POST /auth/token" + route_key = "POST /clientes" + target = "integrations/${aws_apigatewayv2_integration.lambda_backend.id}" +} + +resource "aws_apigatewayv2_route" "clientes_get_route" { + api_id = data.aws_apigatewayv2_api.tc_api.id + route_key = "GET /clientes/{document}" target = "integrations/${aws_apigatewayv2_integration.lambda_backend.id}" } \ No newline at end of file diff --git a/app/src/main/java/tech/buildrun/lambda/HandlerClient.java b/app/src/main/java/tech/buildrun/lambda/HandlerClient.java index 132309e..0fb9695 100644 --- a/app/src/main/java/tech/buildrun/lambda/HandlerClient.java +++ b/app/src/main/java/tech/buildrun/lambda/HandlerClient.java @@ -1,23 +1,19 @@ package tech.buildrun.lambda; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.Keys; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.fasterxml.jackson.databind.ObjectMapper; -import java.nio.charset.StandardCharsets; -import java.util.Date; +import java.sql.*; import java.util.Map; -import javax.crypto.SecretKey; public class HandlerClient implements RequestHandler { - private static final String JWT_SECRET = System.getenv("JWT_SECRET"); - private static final long EXPIRATION_TIME = 3600_000; // 1 hora + private static final String DB_URL = System.getenv("DB_URL"); + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASSWORD = System.getenv("DB_PASSWORD"); private final ObjectMapper mapper = new ObjectMapper(); @@ -25,56 +21,133 @@ public class HandlerClient implements RequestHandler body = - mapper.readValue(request.getBody(), Map.class); + context.getLogger().log("METHOD=" + method); + context.getLogger().log("PATH=" + path); - String username = body.get("user"); + Class.forName("org.postgresql.Driver"); - if (username == null) { - return response(400, Map.of("message", "user obrigatórios")); - } + try (Connection conn = + DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) { - String token = gerarToken(username); + // POST /clientes + if ("POST".equals(method) && "/clientes".equals(path)) { + return criarCliente(request, conn); + } - return response(200, Map.of("token", token)); + // GET /clientes/{document} + if ("GET".equals(method) && path.startsWith("/clientes/")) { + return consultarCliente(path, conn); + } - } catch (Exception e) { - try { - return response(500, Map.of("message", "Erro ao gerar token")); - } catch (Exception ex) { - throw new RuntimeException(ex); + return response(404, Map.of("message", "Endpoint não encontrado")); } + + } catch (Exception e) { + e.printStackTrace(); + return response(500, Map.of("message", "Erro interno")); } } - private String gerarToken(String username) { + // ===================== CRIAR CLIENTE ===================== + + private APIGatewayProxyResponseEvent criarCliente( + APIGatewayProxyRequestEvent request, + Connection conn) throws Exception { + + if (request.getBody() == null || request.getBody().isBlank()) { + return response(400, Map.of("message", "Body obrigatório")); + } + + Map body = mapper.readValue(request.getBody(), Map.class); + + String document = body.get("document"); + String name = body.get("name"); + String email = body.get("email"); + + if (document == null || name == null || email == null) { + return response(400, Map.of( + "message", "document, name e email são obrigatórios" + )); + } - if (JWT_SECRET == null || JWT_SECRET.length() < 32) { - throw new IllegalStateException("JWT_SECRET deve ter no mínimo 32 caracteres"); + String sql = """ + INSERT INTO tb_cliente (nr_documento, nm_cliente, ds_email) + VALUES (?, ?, ?) + """; + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, document); + ps.setString(2, name); + ps.setString(3, email); + ps.executeUpdate(); + + return response(201, Map.of( + "message", "Cliente criado com sucesso", + "document", document + )); + + } catch (SQLException e) { + if ("23505".equals(e.getSQLState())) { + return response(409, Map.of("message", "Cliente já existe")); + } + throw e; } + } + + // ===================== CONSULTAR CLIENTE ===================== + + private APIGatewayProxyResponseEvent consultarCliente( + String path, + Connection conn) throws Exception { + + // /clientes/123456 + String document = path.substring("/clientes/".length()); + + String sql = """ + SELECT id, nr_documento, nm_cliente, ds_email + FROM tb_cliente + WHERE nr_documento = ? + """; + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, document); + ResultSet rs = ps.executeQuery(); - SecretKey key = Keys.hmacShaKeyFor( - JWT_SECRET.getBytes(StandardCharsets.UTF_8) - ); - - return Jwts.builder() - .setSubject(username) - .claim("role", "USER") - .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) - .signWith(key, SignatureAlgorithm.HS256) - .compact(); + if (rs.next()) { + return response(200, Map.of( + "id", rs.getLong("id"), + "document", rs.getString("nr_documento"), + "name", rs.getString("nm_cliente"), + "email", rs.getString("ds_email") + )); + } + + return response(404, Map.of("message", "Cliente não encontrado")); + } } - private APIGatewayProxyResponseEvent response(int status, Object body) throws Exception { - return new APIGatewayProxyResponseEvent() - .withStatusCode(status) - .withHeaders(Map.of("Content-Type", "application/json")) - .withBody(mapper.writeValueAsString(body)); + // ===================== RESPONSE ===================== + + private APIGatewayProxyResponseEvent response(int status, Object body) { + try { + return new APIGatewayProxyResponseEvent() + .withStatusCode(status) + .withHeaders(Map.of( + "Content-Type", "application/json", + "Access-Control-Allow-Origin", "*", + "Access-Control-Allow-Headers", "Content-Type,Authorization", + "Access-Control-Allow-Methods", "GET,POST,OPTIONS" + )) + .withBody(mapper.writeValueAsString(body)); + } catch (Exception e) { + return new APIGatewayProxyResponseEvent() + .withStatusCode(500) + .withBody("{\"message\":\"Erro ao serializar resposta\"}"); + } } } \ No newline at end of file diff --git a/lambda.tf b/lambda.tf index 77c9a9c..129eabc 100644 --- a/lambda.tf +++ b/lambda.tf @@ -8,13 +8,13 @@ data "aws_security_group" "id_lambda" { } resource "aws_lambda_function" "id_lambda" { - function_name = "lambda-identification-auth" + function_name = "lambda-identification-client" depends_on = [] role = data.aws_iam_role.lambda_exec_role.arn - handler = "tech.buildrun.lambda.Handler::handleRequest" + handler = "tech.buildrun.lambda.HandlerClient::handleRequest" runtime = "java17" - timeout = 6 + timeout = 10 # Usa o caminho passado via variável filename = var.lambda_jar_path diff --git a/vars.tf b/vars.tf index b473eb2..285199c 100644 --- a/vars.tf +++ b/vars.tf @@ -7,7 +7,7 @@ variable "region" { variable "tags" { default = { Environment = "PRD" - Project = "tc-lambda-identification-auth" + Project = "tc-lambda-identification-client" } } @@ -31,5 +31,5 @@ variable "jwt_secret" { variable "lambda_jar_path" { description = "Caminho do fat JAR da Lambda" type = string - default = "app/target/lambda-identification-auth.jar" + default = "app/target/lambda-identification-client.jar" } From 1c5c5473a0290ccd213ddd8eb2d90ea5886decee Mon Sep 17 00:00:00 2001 From: carlosbastoslaet Date: Sat, 3 Jan 2026 11:15:46 -0300 Subject: [PATCH 2/2] fix var --- .github/workflows/deploy.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2e915f9..f7f2d6d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -43,7 +43,7 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-secret-access-key: ${{ secrets.AWS_ACCESS_SECRET }} aws-region: us-east-1 # 5️⃣ Setup Terraform @@ -55,14 +55,14 @@ jobs: run: terraform init env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} # 7️⃣ Terraform Plan - name: Terraform Plan run: terraform plan env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} TF_VAR_db_user: ${{ secrets.DB_USER }} TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }} @@ -75,7 +75,7 @@ jobs: run: terraform apply -auto-approve env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} TF_VAR_db_user: ${{ secrets.DB_USER }} TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} TF_VAR_jwt_secret: ${{ secrets.JWT_SECRET_KEY }}