From 5493010cd586dabfe3e1388a79588b2b4abd09ff Mon Sep 17 00:00:00 2001 From: Joaquin Terzano <128100984+JoaquinTerzano@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:01:55 -0300 Subject: [PATCH 1/6] Add gunicorn to requirements Added 'gunicorn' to the list of dependencies. --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index f855af6..f680a08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,6 +37,7 @@ googleapis-common-protos==1.70.0 greenlet==3.2.2 grpcio==1.71.0 grpcio-status==1.71.0 +gunicorn==23.0.0 h11==0.16.0 h2==4.3.0 hpack==4.1.0 @@ -100,3 +101,4 @@ websockets==15.0.1 Werkzeug==3.1.3 wsproto==1.2.0 + From 24b9a12f7bb00e97df6d5c65a292df82c99eb126 Mon Sep 17 00:00:00 2001 From: Joaquin Terzano <128100984+JoaquinTerzano@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:07:01 -0300 Subject: [PATCH 2/6] Refactor Firebase configuration to use environment variables Removed hardcoded Firebase configuration and replaced it with environment variable loading for credentials. --- app.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/app.py b/app.py index 2a10f30..abd1acf 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,7 @@ builtins.unicode = str import os +import json import psycopg2 from firebase_admin import credentials, auth as firebase_auth from flask import Flask, request, jsonify @@ -53,21 +54,6 @@ def shutdown_session(exception=None): """Cierra la sesión de base de datos al finalizar el contexto de la app.""" cerrar_sesion() -# Configuración Firebase -service_account_info = { - "type": os.environ.get("FIREBASE_TYPE"), - "project_id": os.environ.get("FIREBASE_PROJECT_ID"), - "private_key_id": os.environ.get("FIREBASE_PRIVATE_KEY_ID"), - "private_key": str(os.environ.get("FIREBASE_PRIVATE_KEY")).replace('\\n', '\n'), - "client_email": os.environ.get("FIREBASE_CLIENT_EMAIL"), - "client_id": os.environ.get("FIREBASE_CLIENT_ID"), - "auth_uri": os.environ.get("FIREBASE_AUTH_URI"), - "token_uri": os.environ.get("FIREBASE_TOKEN_URI"), - "auth_provider_x509_cert_url": os.environ.get("FIREBASE_AUTH_PROVIDER_X509_CERT_URL"), - "client_x509_cert_url": os.environ.get("FIREBASE_CLIENT_X509_CERT_URL"), - "universe_domain": os.environ.get("FIREBASE_UNIVERSE_DOMAIN") -} - # Inicializar Firebase # cred = credentials.Certificate("firebase/firebase-credentials.json") cred = credentials.Certificate(json.loads(os.environ["FIREBASE_CREDENTIALS"])) @@ -141,4 +127,4 @@ def health_check(): socketio.init_app(app) if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file + app.run(host='0.0.0.0', port=5000, debug=True) From 9e9e051a976341e03f01bac668d89858cf56d733 Mon Sep 17 00:00:00 2001 From: Joaquin Terzano <128100984+JoaquinTerzano@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:25:12 -0300 Subject: [PATCH 3/6] Remove socketio initialization from app.py Removed socketio initialization from the main section. --- app.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app.py b/app.py index abd1acf..d4d05b2 100644 --- a/app.py +++ b/app.py @@ -121,10 +121,5 @@ def handle_options(): def health_check(): return "Ok" - -# MAIN -# Inicializas socketio con la app -socketio.init_app(app) - if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) From 917c0f3473a8bf06a880e4dba15627b958865e1e Mon Sep 17 00:00:00 2001 From: "joaquin.l.terzano@gmail.com" Date: Thu, 4 Dec 2025 18:39:45 -0300 Subject: [PATCH 4/6] quito la mierda de aws --- .aws/redema-back-https-def-revision4.json | 133 ---------------------- .github/workflows/aws.yml | 96 ---------------- 2 files changed, 229 deletions(-) delete mode 100644 .aws/redema-back-https-def-revision4.json delete mode 100644 .github/workflows/aws.yml diff --git a/.aws/redema-back-https-def-revision4.json b/.aws/redema-back-https-def-revision4.json deleted file mode 100644 index 937cb4b..0000000 --- a/.aws/redema-back-https-def-revision4.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "compatibilities": [ - "EC2", - "FARGATE", - "MANAGED_INSTANCES" - ], - "containerDefinitions": [ - { - "cpu": 0, - "environment": [ - { - "name": "DATABASE_URL", - "value": "postgresql://postgres.teryepkwdwweoqwirhfl:redeMaster12312341234554377@aws-1-sa-east-1.pooler.supabase.com:5432/postgres" - }, - { - "name": "CLOUDINARY_API_KEY", - "value": "152753361657899" - }, - { - "name": "CLOUDINARY_CLOUD_NAME", - "value": "redema" - }, - { - "name": "CLOUDINARY_API_SECRET", - "value": "ykSfox6EJCsguW47Ck80onve5Y0" - }, - { - "name": "FIREBASE_CREDENTIALS", - "value": "{\"type\": \"service_account\",\"project_id\": \"redema-4b4ac\",\"private_key_id\": \"55bd02adfb2f8a8dc0e80f76b4abdb0e02c8fec7\",\"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8Qx73LdUGzPlr\\nZtG6bmYWjal2HFXdcTAM/NPhb3vJlnZ0v3j8g4ebhZiv6DlBX1S2dY/UYLAD3g1d\\nx0ZUq1phC8fNNi2ZQb6IEE8rB4WoD5UuoYJzEwY26/RKxl2H1Gyil5lnnY6NhFdE\\nJ6nvpWRc0BMucEdyB+I+Y2HLOP/poAt8ZjDcp/BTM6Q1ZjR1SlWY0O96Q06tnjMP\\nUNSTjmgekgE7GsyI8FehnDB56aSKWSve8/4lrBMiO2nsMYW/A/397TOXtrQDApwP\\nokzWPB+P+znFGR1VrvMIvF86c7o3fX3UKkwyIYxuNJVE530aJWw6r+dTNqd9LYxL\\ns+hilbADAgMBAAECggEAXcz9qT+zQ+uuHHbC6aZnBDa6KAivOaHTRK1WLp066rKm\\nu73JHNu2HeWfMYo9uNY0YaLKspEoMg0Z0M9gugAUjITvnM4xQwqQUb/6iiB51kZJ\\nWM4NLqPcdVH0SqtyX6KWLB7nxsoohFdbmbA/ihF4o4vi+sDjwCuwbNBcogJ48+A3\\nEwy4n1DzjiO4dX6FwjqaonQqAoF5xQweraDBF2WV1KYb1MX6W7bs+w6RJBhjzB2X\\nbNbXzUOylr99Yw3pZCPHLsLp1dT6vPPZAJA0XTo8vCTqlAGDQcoCDfXYwfWsR0iU\\nN8KqIBuTeQmCROX1Hq4Keq5h9pfGKAWuP2GoLlbfsQKBgQD6z6GX5bQfH/NZ5ruw\\nPJxt7+E7w+txIUP9V5fM3inV5SAEe0ZfYuIlee0jgdSkyq0HZ3PfFs5vnaHyA8Ua\\nOHqYB0jwK0aAAgKRG7lIEjzkA741+7XFx7iUsfkOVWwUdAR6zyCPVJdI8I5lfiql\\nv6n8HPDHZe05VfLI3qAZv/IAUwKBgQDAKDZucth/BI33mD9f36mNCcG2paDxXgj/\\nntXnGOs/Pb70o8++sp5durJ2oEVjP6rcPQdHrfz62NOnsBNrBPsrFuXs95wRGeoo\\nd31OjyTALx9q3cBgQCM4xjk37RT9OMs+k6KHxYBbd98OCL1t19M9VuGCZWtrDt5M\\nLEcIVUJbkQKBgQCxeFvKZJIod+40zew+zxSNQfCo3n/pg/Vc/S7/mrAVltQp96Sa\\n+Bg3Fmgy+LojhPocRBqcX8Hg/rRJ0FsWq6IrkvQMZUK0bzerv+dB1Q3a9b8Q6bP8\\nZ873AFvPxaf9bP6Ce695XAzUYssPi8/XiqBYMY46Oag7KtLpSYYV0lkF2wKBgQCB\\ndVYemT8ka/QdgDQKKox3WcBphjLlCl5zoRYpSM+M3tOczBQ1PO+W5CZB5353/Dfn\\ngzI5Z43NjnEiQ20Bp+xzvkyPls5NczEfrEj+uU+gLt8yx01JS0yvflSkShTe+Plt\\nIlfYswXkGeFr+hVtQH+vjq4FhMCW8wBvP1Fn2tEHEQKBgCy3y13W5jS5TIMOszYA\\nyDgI/bSMY7aH8rWOzX8QWthP6BGSnvxag9e+ZTcTun6vcsA58bY4Qa9Ta2H1U/ov\\nlMsbrPAGwZpNAVd40NLUMzhkA4do+edgzADgQFgXVicFuLxse+h0seMm8ZmfH2zv\\nxQUdYOXCBNqzA3++AHyeVsDb\\n-----END PRIVATE KEY-----\\n\",\"client_email\": \"firebase-adminsdk-fbsvc@redema-4b4ac.iam.gserviceaccount.com\",\"client_id\": \"118333764745947718749\",\"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\"token_uri\": \"https://oauth2.googleapis.com/token\",\"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40redema-4b4ac.iam.gserviceaccount.com\",\"universe_domain\": \"googleapis.com\"}" - }, - { - "name": "CLOUDINARY_UPLOAD_PRESET", - "value": "redema_imagenes" - }, - { - "name": "FRONTEND_URL", - "value": "https://redema-front.vercel.app" - } - ], - "environmentFiles": [], - "essential": true, - "healthCheck": { - "command": [ - "CMD-SHELL", - "curl -f http://localhost:5000/ || exit 1" - ], - "interval": 30, - "retries": 3, - "timeout": 5 - }, - "image": "724843234499.dkr.ecr.us-east-2.amazonaws.com/joaquinterzano/redema-back:4d123b73a9a03e173576719a4d23c70c5e598972", - "logConfiguration": { - "logDriver": "awslogs", - "options": { - "awslogs-group": "/ecs/redema-back-https-def", - "awslogs-create-group": "true", - "awslogs-region": "us-east-2", - "awslogs-stream-prefix": "ecs" - }, - "secretOptions": [] - }, - "mountPoints": [], - "name": "redema-back-container", - "portMappings": [ - { - "appProtocol": "http", - "containerPort": 5000, - "hostPort": 5000, - "name": "redema-back-container-5000-tcp", - "protocol": "tcp" - } - ], - "systemControls": [], - "volumesFrom": [] - } - ], - "cpu": "1024", - "executionRoleArn": "arn:aws:iam::724843234499:role/ecsTaskExecutionRole", - "family": "redema-back-https-def", - "memory": "3072", - "networkMode": "awsvpc", - "placementConstraints": [], - "registeredAt": "2025-12-02T07:08:59.039Z", - "registeredBy": "arn:aws:iam::724843234499:root", - "requiresAttributes": [ - { - "name": "com.amazonaws.ecs.capability.logging-driver.awslogs" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.24" - }, - { - "name": "ecs.capability.execution-role-awslogs" - }, - { - "name": "com.amazonaws.ecs.capability.ecr-auth" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19" - }, - { - "name": "com.amazonaws.ecs.capability.task-iam-role" - }, - { - "name": "ecs.capability.container-health-check" - }, - { - "name": "ecs.capability.execution-role-ecr-pull" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18" - }, - { - "name": "ecs.capability.task-eni" - }, - { - "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29" - } - ], - "requiresCompatibilities": [ - "FARGATE" - ], - "revision": 4, - "runtimePlatform": { - "cpuArchitecture": "X86_64", - "operatingSystemFamily": "LINUX" - }, - "status": "ACTIVE", - "taskDefinitionArn": "arn:aws:ecs:us-east-2:724843234499:task-definition/redema-back-https-def:4", - "taskRoleArn": "arn:aws:iam::724843234499:role/ecsTaskExecutionRole", - "volumes": [], - "tags": [] -} \ No newline at end of file diff --git a/.github/workflows/aws.yml b/.github/workflows/aws.yml deleted file mode 100644 index 95ab71b..0000000 --- a/.github/workflows/aws.yml +++ /dev/null @@ -1,96 +0,0 @@ -# This workflow will build and push a new container image to Amazon ECR, -# and then will deploy a new task definition to Amazon ECS, when there is a push to the "main" branch. -# -# To use this workflow, you will need to complete the following set-up steps: -# -# 1. Create an ECR repository to store your images. -# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`. -# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name. -# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region. -# -# 2. Create an ECS task definition, an ECS cluster, and an ECS service. -# For example, follow the Getting Started guide on the ECS console: -# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun -# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service. -# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster. -# -# 3. Store your ECS task definition as a JSON file in your repository. -# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`. -# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file. -# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container -# in the `containerDefinitions` section of the task definition. -# -# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. -# See the documentation for each action used below for the recommended IAM policies for this IAM user, -# and best practices on handling the access key credentials. - -name: Deploy to Amazon ECS - -on: - push: - branches: ["main"] - -env: - AWS_REGION: us-east-2 # set this to your preferred AWS region, e.g. us-west-1 - ECR_REPOSITORY: joaquinterzano/redema-back # set this to your Amazon ECR repository name - ECS_SERVICE: redema-back-https-def-service-de1y74m3 # set this to your Amazon ECS service name - ECS_CLUSTER: redema-back # set this to your Amazon ECS cluster name - ECS_TASK_DEFINITION: - .aws/redema-back-https-def-revision4.json # set this to the path to your Amazon ECS task definition - # file, e.g. .aws/task-definition.json - CONTAINER_NAME: - redema-back-container # set this to the name of the container in the - # containerDefinitions section of your task definition - -permissions: - contents: read - -jobs: - deploy: - name: Deploy - runs-on: ubuntu-latest - environment: production - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 - - - name: Build, tag, and push image to Amazon ECR - id: build-image - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - IMAGE_TAG: ${{ github.sha }} - run: | - # Build a docker container and - # push it to ECR so that it can - # be deployed to ECS. - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT - - - name: Fill in the new image ID in the Amazon ECS task definition - id: task-def - uses: aws-actions/amazon-ecs-render-task-definition@v1 - with: - task-definition: ${{ env.ECS_TASK_DEFINITION }} - container-name: ${{ env.CONTAINER_NAME }} - image: ${{ steps.build-image.outputs.image }} - - - name: Deploy Amazon ECS task definition - uses: aws-actions/amazon-ecs-deploy-task-definition@v1 - with: - task-definition: ${{ steps.task-def.outputs.task-definition }} - service: ${{ env.ECS_SERVICE }} - cluster: ${{ env.ECS_CLUSTER }} - wait-for-service-stability: true From 0994bdaeefeb4ee518f4aa1d20cb2bde6bb3323e Mon Sep 17 00:00:00 2001 From: "joaquin.l.terzano@gmail.com" Date: Thu, 4 Dec 2025 19:28:22 -0300 Subject: [PATCH 5/6] quito los parametros url_prefix de algunos endpoints que fallan con el deploy de gcp sin razon aparente --- components/etiquetas/routes.py | 12 ++++++------ components/roles/routes.py | 14 +++++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/components/etiquetas/routes.py b/components/etiquetas/routes.py index 5dc763c..455ab45 100644 --- a/components/etiquetas/routes.py +++ b/components/etiquetas/routes.py @@ -2,17 +2,17 @@ from core.models import db, Etiqueta from sqlalchemy.exc import IntegrityError -etiquetas_bp = Blueprint('etiquetas', __name__, url_prefix='/api/etiquetas') +etiquetas_bp = Blueprint('etiquetas', __name__) -@etiquetas_bp.route('/', methods=['GET']) +@etiquetas_bp.route('/api/etiquetas', methods=['GET']) def listar_etiquetas(): """Lista todas las etiquetas ordenadas por nombre.""" etiquetas = Etiqueta.query.order_by(Etiqueta.nombre).all() return jsonify([{"id": e.id, "nombre": e.nombre} for e in etiquetas]) -@etiquetas_bp.route('/', methods=['POST']) +@etiquetas_bp.route('/api/etiquetas', methods=['POST']) def crear_etiqueta(): """Crea una nueva etiqueta si el nombre es válido y no existe.""" data = request.json @@ -33,7 +33,7 @@ def crear_etiqueta(): return jsonify({"id": etiqueta.id, "nombre": etiqueta.nombre}), 201 -@etiquetas_bp.route('/', methods=['GET']) +@etiquetas_bp.route('/api/etiquetas/', methods=['GET']) def obtener_etiqueta(id_etiqueta): """Obtiene una etiqueta por su ID.""" etiqueta = Etiqueta.query.get(id_etiqueta) @@ -42,7 +42,7 @@ def obtener_etiqueta(id_etiqueta): return jsonify({"id": etiqueta.id, "nombre": etiqueta.nombre}) -@etiquetas_bp.route('/', methods=['PATCH']) +@etiquetas_bp.route('/api/etiquetas/', methods=['PATCH']) def actualizar_etiqueta(id_etiqueta): """Actualiza el nombre de una etiqueta existente.""" etiqueta = Etiqueta.query.get(id_etiqueta) @@ -67,7 +67,7 @@ def actualizar_etiqueta(id_etiqueta): return jsonify({"id": etiqueta.id, "nombre": etiqueta.nombre}) -@etiquetas_bp.route('/', methods=['DELETE']) +@etiquetas_bp.route('/api/etiquetas/', methods=['DELETE']) def eliminar_etiqueta(id_etiqueta): """Elimina una etiqueta por su ID.""" etiqueta = Etiqueta.query.get(id_etiqueta) diff --git a/components/roles/routes.py b/components/roles/routes.py index 23837ca..d59ca6e 100644 --- a/components/roles/routes.py +++ b/components/roles/routes.py @@ -1,14 +1,16 @@ from flask import Blueprint, request, jsonify from .services import obtener_roles, crear_rol, actualizar_rol, eliminar_rol -roles_bp = Blueprint("roles", __name__, url_prefix="/api/roles") +roles_bp = Blueprint("roles", __name__) -@roles_bp.route("/", methods=["GET"]) + +@roles_bp.route("/api/roles", methods=["GET"]) def listar_roles(): roles = obtener_roles() return jsonify(roles), 200 -@roles_bp.route("/", methods=["POST"]) + +@roles_bp.route("/api/roles", methods=["POST"]) def nuevo_rol(): data = request.get_json() nombre = data.get("nombre") @@ -20,7 +22,8 @@ def nuevo_rol(): return jsonify(rol), 201 return jsonify({"error": "No se pudo crear el rol"}), 500 -@roles_bp.route("/", methods=["PUT"]) + +@roles_bp.route("/api/roles/", methods=["PUT"]) def editar_rol(id): data = request.get_json() nombre = data.get("nombre") @@ -32,7 +35,8 @@ def editar_rol(id): return jsonify(rol), 200 return jsonify({"error": "Rol no encontrado"}), 404 -@roles_bp.route("/", methods=["DELETE"]) + +@roles_bp.route("/api/roles/", methods=["DELETE"]) def borrar_rol(id): ok = eliminar_rol(id) if ok: From 8ef5b70f0629bed3226d4dd90374c6926cda589a Mon Sep 17 00:00:00 2001 From: Joaquin Terzano <128100984+JoaquinTerzano@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:39:03 -0300 Subject: [PATCH 6/6] cambio para enviar el estado de la publicacion al front, y arreglar bug de archivado en mi perfil --- components/publicaciones/services.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/publicaciones/services.py b/components/publicaciones/services.py index 8f9b2ab..d1adb31 100644 --- a/components/publicaciones/services.py +++ b/components/publicaciones/services.py @@ -37,6 +37,7 @@ def serializar_publicacion_lista(pub): "etiquetas": [et.nombre for et in pub.etiquetas], "fecha_creacion": pub.fecha_creacion.astimezone(zona_arg).isoformat() if pub.fecha_creacion else None, "coordenadas": pub.coordenadas, + "estado": pub.estado, # No enviamos descripción completa para ahorrar datos en listas } @@ -377,4 +378,4 @@ def subir_imagen_a_cloudinary(file): return result.get("secure_url") except Exception as e: print("Error Cloudinary:", str(e)) - return None \ No newline at end of file + return None