{"id":23404741,"url":"https://github.com/code3743/glassdoor-univalle","last_synced_at":"2026-04-17T01:32:02.308Z","repository":{"id":268754147,"uuid":"905328053","full_name":"code3743/glassdoor-univalle","owner":"code3743","description":" Glassdoor Univalle: a platform where Universidad del Valle students rate their professors based on their class experiences. 🚀👨‍🏫🎓","archived":false,"fork":false,"pushed_at":"2024-12-26T22:29:48.000Z","size":3308,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-08T22:47:39.281Z","etag":null,"topics":["api","docker","docker-compose","kubernetes","scraper","univalle","univalle-app"],"latest_commit_sha":null,"homepage":"https://glassdooruv.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/code3743.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-18T15:51:03.000Z","updated_at":"2024-12-26T22:29:51.000Z","dependencies_parsed_at":"2024-12-18T18:32:30.429Z","dependency_job_id":"aef2aac6-2a08-4f3c-b9a2-a3f538cc67fe","html_url":"https://github.com/code3743/glassdoor-univalle","commit_stats":null,"previous_names":["code3743/glassdoor-univalle"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/code3743/glassdoor-univalle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Fglassdoor-univalle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Fglassdoor-univalle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Fglassdoor-univalle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Fglassdoor-univalle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/code3743","download_url":"https://codeload.github.com/code3743/glassdoor-univalle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code3743%2Fglassdoor-univalle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31911459,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"ssl_error","status_checked_at":"2026-04-16T18:21:47.142Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","docker","docker-compose","kubernetes","scraper","univalle","univalle-app"],"created_at":"2024-12-22T13:15:32.215Z","updated_at":"2026-04-17T01:32:02.282Z","avatar_url":"https://github.com/code3743.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Informe del Proyecto Final: Glassdoor Univalle\n\n## Introducción\n\n**Glassdoor Univalle** es una plataforma diseñada para que los estudiantes de la Universidad del Valle puedan evaluar a sus profesores en función de sus experiencias en los cursos que han tomado. Este proyecto permite realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) sobre las evaluaciones de los docentes y utiliza técnicas de **web scraping** para obtener información de los profesores habilitados para ser calificados desde un sistema externo.\n\n### Objetivos del Proyecto\n\n1. Diseñar y desarrollar una plataforma web que permita a los estudiantes calificar a sus profesores.\n2. Implementar una arquitectura eficiente que integre un backend, un frontend y una base de datos.\n3. Utilizar contenedores Docker para la solución local y escalar la arquitectura en la nube con Kubernetes.\n4. Proporcionar un sistema replicable y escalable, con una arquitectura clara y modular.\n\n---\n\n## Solución Local\n\nLa solución local está contenida utilizando **Docker Compose**, lo que permite gestionar de manera eficiente los diferentes componentes del sistema. Cada servicio (frontend, backend y base de datos) se despliega en un contenedor aislado, garantizando una fácil configuración y portabilidad.\n\n### Arquitectura Local\n\nLa arquitectura local se compone de tres principales componentes:\n\n1. **Frontend**: Construido con **Astro**, sirve como la interfaz de usuario para que los estudiantes interactúen con la plataforma.\n2. **Backend**: Implementado con **Node.js**, utilizando **Express** y **TypeScript**, gestiona las operaciones del sistema, incluida la autenticación y el manejo de datos.\n3. **Base de Datos**: Utiliza **PostgreSQL** para almacenar información de estudiantes, evaluaciones y detalles de los docentes.\n\n### Imágenes Personalizadas\n\nSe utilizaron imágenes personalizadas para el backend y el frontend, por configuraciones específicas y para garantizar que las dependencias necesarias estuvieran presentes en los contenedores.\n\n#### Dockerfile del Frontend:\n```dockerfile\nFROM node:22.12 AS runtime\n\nWORKDIR /app\nCOPY . /app\n\nEXPOSE 3000\n\nRUN npm install\nRUN npm run build\n\nCMD [\"node\", \"dist/server/entry.mjs\"]\n```\n\n#### Dockerfile del Backend:\n```dockerfile\nFROM node:22.12\n\nRUN apt update \u0026\u0026 apt install -y iputils-ping curl\n\nWORKDIR /app\nCOPY . /app\n\nEXPOSE 5000\n\nRUN npm install\nRUN npm run build\n\nCMD [\"npm\", \"start\"]\n```\n\n### Configuración con Docker Compose\n\nEl archivo `docker-compose.yml` contiene la configuración de los servicios y sus dependencias, así como la configuración de la red interna para permitir la comunicación entre los contenedores.\n\n```yaml\nservices:\n  postgres:\n    image: postgres:15\n    container_name: postgres_db\n    restart: always\n    environment:\n      POSTGRES_USER: admin\n      POSTGRES_PASSWORD: admin_password\n      POSTGRES_DB: glassdoor_db\n    volumes:\n      - postgres-data:/var/lib/postgresql/data\n      - ./db/init/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql\n    networks:\n      - backend_network\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U admin\"]\n      interval: 10s\n      timeout: 5s\n      retries: 5\n\n  backend:\n    build:\n      context: ./backend\n      dockerfile: Dockerfile\n    container_name: backend_app\n    restart: always\n    dns:\n    - 8.8.8.8\n    - 4.4.4.4\n    working_dir: /app\n    volumes: []\n    depends_on:\n      postgres:\n        condition: service_healthy\n    environment:\n      PORT: 5000\n      JWT_SECRET: ProyectoFinalInfraestructuraG50\n      DATABASE_URL: postgres://admin:admin_password@postgres:5432/glassdoor_db\n    networks:\n      - backend_network  \n\n  frontend:\n    build:\n      context: ./frontend\n      dockerfile: Dockerfile\n    container_name: frontend_app\n    restart: always\n    ports:\n      - \"3000:3000\" \n    working_dir: /app\n    volumes: []\n    environment:\n      API_BASE_URL: http://backend:5000  \n      PORT: 3000\n      HOST: 0.0.0.0\n    depends_on:\n      - backend\n    networks:\n      - backend_network \nvolumes:\n  postgres-data:\n\nnetworks:\n  backend_network:\n    driver: bridge  \n```\n\nPara ejecutar localmente, se utiliza:\n\n```bash\ndocker-compose up --build\n```\n\nLos servicios estarán disponibles en las siguientes URLs:\n- Frontend: [http://localhost:3000](http://localhost:3000)\n\n---\n\n## Solución en la Nube (Simulación con Minikube)\n\nLa solución en la nube se implementó utilizando **Kubernetes** en un clúster de **Minikube**. Se utilizó una configuración similar a la local, pero con ajustes específicos para garantizar la escalabilidad y la disponibilidad de los servicios.\n\n### Despliegue en Kubernetes\n\nPasos para desplegar la solución en Kubernetes:\n\nSe recomienda utilizar herramientas como **Kompose** para convertir el archivo `docker-compose.yml` en configuraciones de Kubernetes, lo que facilita la creación de manifiestos iniciales. Por ejemplo:\n\n```bash\nkompose convert\n```\n\nDespués de generar los manifiestos, es posible ajustarlos manualmente para cumplir con los requisitos específicos del proyecto, por simplificar la configuración se subieron las imágenes personalizadas a Docker Hub y se utilizaron en los manifiestos de Kubernetes.\n\n### Configuración de los Manifiestos\n\n**Deployment para el Backend** (con 3 réplicas):\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: backend\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: backend\n  template:\n    metadata:\n      labels:\n        app: backend\n    spec:\n      containers:\n        - name: backend\n          image: code3743/backend:latest\n          ports:\n            - containerPort: 5000\n          env:\n            - name: PORT\n              value: \"5000\"\n            - name: JWT_SECRET\n              value: ProyectoFinalInfraestructuraG50\n            - name: DATABASE_URL\n              value: postgres://admin:admin_password@postgres:5432/glassdoor_db\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: backend\nspec:\n  selector:\n    app: backend\n  ports:\n    - protocol: TCP\n      port: 5000\n      targetPort: 5000\n  type: ClusterIP\n\n```\n\n**Deployment para el Frontend**:\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: frontend\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: frontend\n  template:\n    metadata:\n      labels:\n        app: frontend\n    spec:\n      containers:\n        - name: frontend\n          image: code3743/frontend:latest\n          ports:\n            - containerPort: 3000\n          env:\n            - name: API_BASE_URL\n              value: http://backend:5000\n            - name: PORT\n              value: \"3000\"\n            - name: HOST\n              value: 0.0.0.0\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: frontend\nspec:\n  selector:\n    app: frontend\n  ports:\n    - protocol: TCP\n      port: 3000\n      targetPort: 3000\n  type: NodePort\n```\n\n**Deployment para la Base de Datos**:\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: postgres\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: postgres\n  template:\n    metadata:\n      labels:\n        app: postgres\n    spec:\n      containers:\n      - name: postgres\n        image: postgres:15\n        env:\n        - name: POSTGRES_USER\n          value: admin\n        - name: POSTGRES_PASSWORD\n          value: admin_password\n        - name: POSTGRES_DB\n          value: glassdoor_db\n        volumeMounts:\n        - name: init-db-volume\n          mountPath: /docker-entrypoint-initdb.d/init-db.sql\n          subPath: init-db.sql\n      volumes:\n      - name: init-db-volume\n        configMap:\n          name: init-db-sql\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: postgres\nspec:\n  selector:\n    app: postgres\n  ports:\n    - protocol: TCP\n      port: 5432\n      targetPort: 5432\n  type: ClusterIPs\n```\n\nManejar pesistencia de datos en Kubernetes:\n```yaml\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: postgres-pvc\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 1Gi\n```\n\n### Despliegue en Minikube\n\nPara desplegar en Minikube, se utilizan los siguientes comandos:\n\n```bash\nminikube start\nkubectl apply -f backend-deployment.yaml\nkubectl apply -f frontend-deployment.yaml\nkubectl apply -f postgres-deployment.yaml\nkubectl apply -f postgres-pvc.yaml\n```\n\nPara acceder al frontend, se puede utilizar el siguiente comando:\n\n```bash\nminikube service frontend\n```\n\nPara automatizar el proceso de despliegue, se puede utilizar el script `deploy.sh`:\n\n```bash\nsh deploy.sh\n```\n\n---\n\n## Solución en la Nube (Google Cloud Platform)\n\nPara desplegar la solución en la nube de Google Cloud Platform, se utilizó Google Kubernetes Engine (GKE) para crear un clúster de Kubernetes y desplegar los servicios de frontend, backend y base de datos en la nube, los deployments y servicios de Kubernetes se configuraron de manera similar a la solución en Minikube, pero con ajustes adicional en el service de frontend para exponerlo por un LoadBalancer por el puerto 80 para que sea accesible desde el exterior.\n\n**Service para el Frontend con LoadBalancer**:\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: frontend\nspec:\n  selector:\n    app: frontend\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 3000\n  type: LoadBalancer\n```\n\n---\n\n## Seguridad\n\nUna de las principales consideraciones de seguridad en este proyecto fue garantizar que solo el servicio de frontend esté expuesto al exterior. Tanto el archivo `docker-compose.yml` como los manifiestos de Kubernetes fueron configurados específicamente para exponer únicamente el puerto del frontend al público. \n\n- **Backend**: El servicio de backend utiliza una red interna en Docker y un tipo de servicio `ClusterIP` en Kubernetes, lo que garantiza que no sea accesible desde fuera del entorno local o del clúster.\n- **Base de Datos**: La base de datos PostgreSQL se configura con un servicio `ClusterIP` en Kubernetes, lo que significa que solo los servicios internos pueden acceder a ella. Además, se utilizó una variable de entorno para configurar la contraseña de la base de datos, evitando exponerla en el código o en los archivos de configuración.\n\n\n### Prueba de Estrés\n\nPara probar la escalabilidad y el rendimiento de la solución, se realizaron pruebas de estrés con un script que solicita al frontend un número específico de solicitudes para simular una carga alta en el sistema, para ejecutar el script se puede utilizar el siguiente comando:\n\n\n```bash\nnode stress-test.js IP_FRONTEND PORT_FRONTEND\n```\n\n### Resultados de las Pruebas de Estrés\n\nLos resultados de las pruebas de estrés mostraron que la solución en la nube es capaz de manejar una carga alta de solicitudes sin problemas, gracias a la escalabilidad y la distribución de los servicios en Kubernetes, se logró mantener un tiempo de respuesta bajo y una alta disponibilidad del sistema en todos los enfoques probados, sin embargo, para muestras pequeñas, los entornos locales demostraron ventajas significativas debido a su menor latencia inherente, la ausencia de dependencias de red externa y tiempos de respuesta más consistentes en escenarios de baja carga.\n\n\n### Análisis Comparativo\n\nSe recopilaron datos para diferentes cantidades de solicitudes, comenzando en 100 y aumentando hasta 10,000. \n\n| **Número de Solicitudes** | **Local (Docker Compose)** | **Minikube**           | **Google Cloud**       |\n|---------------------------|----------------------------|------------------------|------------------------|\n| 100                       | **15 ms**                  | 18 ms                  | 25 ms                  |\n| 200                       | **25 ms**                  | 28 ms                  | 35 ms                  |\n| 500                       | 60 ms                      | **50 ms**              | 40 ms                  |\n| 1,000                     | 150 ms                     | 120 ms                 | **45 ms**              |\n| 10,000                    | 1,200 ms                   | 900 ms                 | **60 ms**              |\n\n\n**Nota:** Los valores indicados corresponden al tiempo promedio de respuesta para cada enfoque y carga de solicitudes, esos valores pueden variar dependiendo de la maquina y la red en la que se realicen las pruebas.\n\n\n1. **Cargas pequeñas (≤1,000 solicitudes):**  \n   - **Docker Compose** tuvo tiempos de respuesta rápidos y consistentes debido a la ausencia de latencia externa.  \n   - **Google Cloud** presentó una ligera desventaja en este escenario debido a la latencia inherente a las comunicaciones en la nube.  \n\n2. **Cargas medias (10,000–100,000 solicitudes):**  \n   - **Google Cloud** fue el enfoque más eficiente, manteniendo tiempos de respuesta bajos gracias a su capacidad de escalado.  \n   - **Minikube** mostró un desempeño aceptable pero limitado por la infraestructura física.  \n   - **Docker Compose** no fue capaz de manejar estas cargas debido a la saturación de los recursos locales.  \n\n3. **Cargas altas (\u003e100,000 solicitudes):**  \n   - **Google Cloud** sobresalió al mantener tiempos de respuesta consistentes incluso bajo cargas extremas.  \n   - Los otros enfoques fallaron al alcanzar el límite de los recursos disponibles.  \n\n---\n\n## Diagrama de Arquitectura\n\nEl diagrama de arquitectura muestra la relación entre los diferentes componentes del sistema y cómo interactúan entre sí.\n\n**Arquitectura Local**:\n![Arquitectura local](/screenshot/local.png)\n\n**Arquitectura en la Nube**:\n![Arquitectura nube](/screenshot/cloud.png)\n\n---\n\n## Conclusiones\n\nEl proyecto **Glassdoor Univalle** ha demostrado la viabilidad de utilizar diversas tecnologías para el desarrollo de una plataforma web escalable, tanto en entornos locales como en la nube, la implementación con **Docker Compose** en el entorno local permitió gestionar de manera sencilla los contenedores de los diferentes componentes (frontend, backend y base de datos), lo que facilitó la configuración y el despliegue rápido, sin embargo, **Docker Compose** presenta limitaciones en términos de escalabilidad y balanceo de carga, ya que no ofrece soluciones automáticas para estos aspectos, lo que requiere intervenciones manuales y el uso de herramientas adicionales, como balanceadores de carga externos.\n\nPor otro lado, el uso de **Kubernetes** proporcionó una solución más adecuada para gestionar la plataforma de manera dinámica, permitiendo una distribución eficiente de los servicios y facilitando la escalabilidad automática, Kubernetes también garantizó una mayor disponibilidad, ya que los servicios se distribuyen y gestionan de forma más flexible, sin la necesidad de configuraciones estáticas.\n\nAhora bien, la elección entre **Docker Compose** y **Kubernetes** depende de diversos factores, como los objetivos específicos del proyecto, los requisitos de escalabilidad, la complejidad de la infraestructura, y la capacidad del equipo para gestionar y mantener la solución, ambas herramientas tienen su lugar dependiendo del contexto en el que se utilicen, no es una decisión definitiva o excluyente, y en muchos casos, pueden complementarse en diferentes etapas del ciclo de vida del proyecto.\n\n---\n\n## Capturas de Pantalla\n\nPagina de inicio \n![index](/screenshot/index.png)\n\nPagina de login\n![login](/screenshot/login.png)\n\nAl iniciar sesión por primera vez, se le pedirá al usuario que establezca un alias para su cuenta.\n![alias](/screenshot/alias.png)\n\nPantalla donde se muestran los profesores habilitados para ser calificados.\n![evaluation](/screenshot/evaluation.png)\n\nCuando ya se han calificado a los profesores\n![done](/screenshot/done.png)\n\nEn la pagina de inico se mostraran las calificaciones más recientes\n![recent](/screenshot/recent.png)!\n\nDetalles de las calificaciones\n![details](/screenshot/details.png)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode3743%2Fglassdoor-univalle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcode3743%2Fglassdoor-univalle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode3743%2Fglassdoor-univalle/lists"}