[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81334":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":9,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":17,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":13,"lastSyncTime":24,"discoverSource":25},81334,"PP-AI","DeveloperSolver\u002FPP-AI","DeveloperSolver","AI-Powered Project Management Tool",null,"TypeScript",43,19,2,0,1,3.9,false,"main",true,[],"2026-06-12 02:04:13","# PP-AI — Guía de Despliegue\n\n## Inicio rápido — Docker Desktop (local)\n\n> Requisito: tener **Docker Desktop** instalado y corriendo.\n\n```bash\n# 1. Renombra el archivo .env.example a .env y modifica tus variables\n\n# 2. Levantar toda la aplicación (construye las imágenes la primera vez)\nmake up\n\n# 3. Cargar usuarios de ejemplo\nmake seed\n\n# 4. Abrir en el navegador\nopen http:\u002F\u002Flocalhost:8080\n```\n\n| Cuenta | Contraseña | Rol |\n|---|---|---|\n| admin@app.com | admin123 | Administrador |\n| editor@app.com | editor123 | Editor |\n| viewer@app.com | viewer123 | Solo lectura |\n\n**Otros comandos útiles:**\n\n```bash\nmake logs          # Ver logs en tiempo real\nmake ps            # Estado de los contenedores\nmake down          # Detener la aplicación\nmake reset         # ⚠ Borrar todo (incluye datos)\nmake shell-backend # Terminal dentro del backend\nmake shell-db      # psql dentro de la base de datos\n```\n\n**Puertos disponibles en local:**\n\n| Servicio | URL |\n|---|---|\n| Aplicación (frontend) | http:\u002F\u002Flocalhost:80 |\n| API backend (directo) | http:\u002F\u002Flocalhost:3003 |\n| PostgreSQL | localhost:5435 (user: `ppai`, password definida en `.env`) |\n\n---\n\n## Guía de Despliegue en Kubernetes\n\n## Contenido\n\n- [Arquitectura](#arquitectura)\n- [Requisitos previos](#requisitos-previos)\n- [Estructura del repositorio](#estructura-del-repositorio)\n- [Paso 1 — Preparar el cluster](#paso-1--preparar-el-cluster)\n- [Paso 2 — Configurar secretos y variables](#paso-2--configurar-secretos-y-variables)\n- [Paso 3 — Ajustar el storageClass](#paso-3--ajustar-el-storageclass)\n- [Paso 4 — Configurar el dominio](#paso-4--configurar-el-dominio)\n- [Paso 5 — Desplegar](#paso-5--desplegar)\n- [Paso 6 — Verificar el despliegue](#paso-6--verificar-el-despliegue)\n- [Paso 7 — Cargar datos iniciales (seed)](#paso-7--cargar-datos-iniciales-seed)\n- [TLS \u002F HTTPS con Let's Encrypt](#tls--https-con-lets-encrypt)\n- [Backup y Restore de la base de datos](#backup-y-restore-de-la-base-de-datos)\n- [Actualizar la aplicación](#actualizar-la-aplicación)\n- [Referencia de imágenes Docker Hub](#referencia-de-imágenes-docker-hub)\n- [Referencia de manifests](#referencia-de-manifests)\n- [Troubleshooting](#troubleshooting)\n\n---\n\n## Arquitectura\n\n```\nInternet\n    │\n    ▼\n┌─────────────────────────────────────┐\n│  Ingress NGINX  (ppai.dominio.com)  │\n└──────────┬──────────────────────────┘\n           │ \u002F          │ \u002Fapi  \u002Fuploads\n           ▼            ▼\n    ┌──────────┐  ┌───────────┐\n    │ Frontend │  │  Backend  │──► PostgreSQL\n    │  (nginx) │  │ (Node.js) │    StatefulSet\n    │ 2 répl.  │  │  1 répl.  │\n    └──────────┘  └───────────┘\n```\n\n| Componente | Imagen | Réplicas |\n|---|---|---|\n| Frontend | `REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-frontend:latest` | 2 (HPA: 2–6) |\n| Backend | `REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-backend:latest` | 1 |\n| Base de datos | `postgres:16-alpine` | 1 (StatefulSet) |\n\n> El backend se mantiene en 1 réplica porque el volumen de uploads es `ReadWriteOnce`.\n> Para escalar el backend se requiere migrar los uploads a object storage (S3, Azure Blob, GCS).\n\n---\n\n## Requisitos previos\n\n### Herramientas locales\n\n| Herramienta | Versión mínima | Instalación |\n|---|---|---|\n| `kubectl` | 1.28 | https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Ftools\u002F |\n| acceso al cluster | — | `kubectl cluster-info` |\n\n### Componentes en el cluster\n\nLos siguientes componentes deben estar instalados **antes** de aplicar los manifests:\n\n#### 1. NGINX Ingress Controller\n```bash\nkubectl apply -f https:\u002F\u002Fraw.githubusercontent.com\u002Fkubernetes\u002Fingress-nginx\u002Fcontroller-v1.10.1\u002Fdeploy\u002Fstatic\u002Fprovider\u002Fcloud\u002Fdeploy.yaml\n\n# Verificar que esté listo\nkubectl wait --namespace ingress-nginx \\\n  --for=condition=ready pod \\\n  --selector=app.kubernetes.io\u002Fcomponent=controller \\\n  --timeout=120s\n```\n\n#### 2. Metrics Server (requerido para HPA)\n```bash\nkubectl apply -f https:\u002F\u002Fgithub.com\u002Fkubernetes-sigs\u002Fmetrics-server\u002Freleases\u002Flatest\u002Fdownload\u002Fcomponents.yaml\n\n# Verificar\nkubectl get deployment metrics-server -n kube-system\n```\n\n#### 3. cert-manager (opcional — solo para TLS automático)\n```bash\nkubectl apply -f https:\u002F\u002Fgithub.com\u002Fcert-manager\u002Fcert-manager\u002Freleases\u002Flatest\u002Fdownload\u002Fcert-manager.yaml\n\n# Verificar\nkubectl wait --namespace cert-manager \\\n  --for=condition=ready pod \\\n  --selector=app.kubernetes.io\u002Finstance=cert-manager \\\n  --timeout=120s\n```\n\n---\n\n## Estructura del repositorio\n\n```\nPP-AI-DEPLOY\u002F\n├── docker-compose.yml          # Deploy con Docker Compose (alternativa local)\n├── .env.example                # Plantilla de variables de entorno\n├── backend\u002F                    # Código fuente + Dockerfile del backend\n├── frontend\u002F                   # Código fuente + Dockerfile del frontend\n└── k8s\u002F                        # Manifests de Kubernetes (aplicar en orden)\n    ├── 01-namespace.yaml           Namespace \"ppai\"\n    ├── 02-secrets.yaml             Contraseñas y tokens  ⚠ editar antes de aplicar\n    ├── 03-configmap.yaml           Config no-sensible (hosts, puertos, flags)\n    ├── 04-database-statefulset.yaml  PostgreSQL 16 con volumen persistente\n    ├── 05-database-service.yaml    Service headless para el StatefulSet\n    ├── 06-backend-pvc.yaml         Volumen para archivos subidos (\u002Fapp\u002Fuploads)\n    ├── 07-backend-deployment.yaml  API Node.js + initContainer wait-for-db\n    ├── 08-backend-service.yaml     ClusterIP :3001\n    ├── 09-frontend-configmap-nginx.yaml  nginx.conf con hostname K8s correcto\n    ├── 10-frontend-deployment.yaml Frontend Nginx, 2 réplicas\n    ├── 11-frontend-service.yaml    ClusterIP :80\n    ├── 12-ingress.yaml             Ingress NGINX con rate-limit y CORS\n    ├── 13-hpa.yaml                 Autoscaling frontend (2–6 réplicas)\n    ├── 14-networkpolicy.yaml       Tráfico mínimo-privilegio entre pods\n    ├── 15-backup-pvc.yaml          Volumen 20 Gi para los dumps de Postgres\n    ├── 16-backup-cronjob.yaml      pg_dump diario a las 02:00 hora local\n    ├── 17-restore-job.yaml         Job de restore manual (plantilla)\n    └── 18-seed-job.yaml            Carga datos de ejemplo (primer deploy)\n```\n\n---\n\n## Paso 1 — Preparar el cluster\n\nVerificar que `kubectl` apunta al cluster correcto:\n\n```bash\nkubectl config current-context\nkubectl cluster-info\n```\n\nPara cambiar de contexto si hay varios clusters configurados:\n\n```bash\nkubectl config get-contexts\nkubectl config use-context \u003Cnombre-del-contexto>\n```\n\n---\n\n## Paso 2 — Configurar secretos y variables\n\n### 2.1 Secretos (`k8s\u002F02-secrets.yaml`)\n\nAbrir el archivo y reemplazar **todos** los valores `CAMBIAR_*`:\n\n```yaml\nstringData:\n  DB_PASSWORD: \"CAMBIAR_PASSWORD_POSTGRES\"    # ← contraseña fuerte\n  JWT_SECRET:  \"CAMBIAR_JWT_SECRET_CON_32_CHARS_MINIMO\"  # ← mínimo 32 chars\n  SMTP_HOST:   \"\"   # opcional\n  SMTP_USER:   \"\"   # opcional\n  SMTP_PASS:   \"\"   # opcional\n  GOOGLE_CLIENT_ID:     \"\"  # opcional\n  GOOGLE_CLIENT_SECRET: \"\"  # opcional\n```\n\nGenerar un JWT_SECRET seguro:\n```bash\nopenssl rand -hex 32\n```\n\n> **Importante:** Este archivo no debe commitearse con valores reales.\n> En entornos de producción utilizar [Sealed Secrets](https:\u002F\u002Fgithub.com\u002Fbitnami-labs\u002Fsealed-secrets)\n> o [External Secrets Operator](https:\u002F\u002Fexternal-secrets.io\u002F) en lugar de este archivo.\n\n### 2.2 ConfigMap (`k8s\u002F03-configmap.yaml`)\n\nActualizar el dominio en las dos líneas indicadas:\n\n```yaml\ndata:\n  FRONTEND_URL:        \"https:\u002F\u002Fppai.midominio.com\"   # ← tu dominio real\n  GOOGLE_CALLBACK_URL: \"https:\u002F\u002Fppai.midominio.com\u002Fapi\u002Fauth\u002Fgoogle\u002Fcallback\"\n```\n\n---\n\n## Paso 3 — Ajustar el storageClass\n\nLos tres PVCs usan `storageClassName: \"standard\"`. Cambiar al valor correcto según el cluster:\n\n| Plataforma | storageClassName |\n|---|---|\n| K3s \u002F Rancher Desktop | `local-path` |\n| AWS EKS | `gp3` |\n| Azure AKS | `managed-premium` |\n| GCP GKE | `standard-rwo` |\n| On-premise | verificar con `kubectl get storageclass` |\n\nArchivos a modificar:\n- `k8s\u002F04-database-statefulset.yaml` (volumeClaimTemplate)\n- `k8s\u002F06-backend-pvc.yaml`\n- `k8s\u002F15-backup-pvc.yaml`\n\nEjemplo para K3s:\n```bash\n# Reemplazar en los tres archivos de una vez\nsed -i 's\u002FstorageClassName: \"standard\"\u002FstorageClassName: \"local-path\"\u002Fg' \\\n  k8s\u002F04-database-statefulset.yaml \\\n  k8s\u002F06-backend-pvc.yaml \\\n  k8s\u002F15-backup-pvc.yaml\n```\n\n---\n\n## Paso 4 — Configurar el dominio\n\nEditar `k8s\u002F12-ingress.yaml` y reemplazar `ppai.midominio.com` por el dominio real:\n\n```yaml\nrules:\n  - host: ppai.midominio.com   # ← CAMBIAR\n```\n\nObtener la IP pública del Ingress Controller para configurar el DNS:\n```bash\nkubectl get svc -n ingress-nginx ingress-nginx-controller\n# Copiar la columna EXTERNAL-IP y crear un registro A en tu proveedor DNS\n```\n\n---\n\n## Paso 5 — Desplegar\n\nAplicar todos los manifests en orden numérico:\n\n```bash\nkubectl apply -f k8s\u002F\n```\n\n`kubectl apply -f k8s\u002F` aplica los archivos en orden alfabético, lo que respeta el orden numérico `01-` → `18-`.\n\nO aplicar uno a uno para mayor control:\n\n```bash\nkubectl apply -f k8s\u002F01-namespace.yaml\nkubectl apply -f k8s\u002F02-secrets.yaml\nkubectl apply -f k8s\u002F03-configmap.yaml\nkubectl apply -f k8s\u002F04-database-statefulset.yaml\nkubectl apply -f k8s\u002F05-database-service.yaml\nkubectl apply -f k8s\u002F06-backend-pvc.yaml\nkubectl apply -f k8s\u002F07-backend-deployment.yaml\nkubectl apply -f k8s\u002F08-backend-service.yaml\nkubectl apply -f k8s\u002F09-frontend-configmap-nginx.yaml\nkubectl apply -f k8s\u002F10-frontend-deployment.yaml\nkubectl apply -f k8s\u002F11-frontend-service.yaml\nkubectl apply -f k8s\u002F12-ingress.yaml\nkubectl apply -f k8s\u002F13-hpa.yaml\nkubectl apply -f k8s\u002F14-networkpolicy.yaml\nkubectl apply -f k8s\u002F15-backup-pvc.yaml\nkubectl apply -f k8s\u002F16-backup-cronjob.yaml\n```\n\n---\n\n## Paso 6 — Verificar el despliegue\n\n```bash\n# Ver todos los recursos del namespace\nkubectl get all -n ppai\n\n# Salida esperada:\n# NAME                                  READY   STATUS    RESTARTS\n# pod\u002Fppai-backend-xxxxx                1\u002F1     Running   0\n# pod\u002Fppai-frontend-xxxxx               1\u002F1     Running   0\n# pod\u002Fppai-frontend-xxxxx               1\u002F1     Running   0\n# pod\u002Fppai-database-0                   1\u002F1     Running   0\n#\n# NAME                   TYPE        CLUSTER-IP    PORT(S)\n# service\u002Fppai-backend   ClusterIP   10.x.x.x      3001\u002FTCP\n# service\u002Fppai-frontend  ClusterIP   10.x.x.x      80\u002FTCP\n# service\u002Fppai-database  ClusterIP   None           5432\u002FTCP\n#\n# NAME                             READY   UP-TO-DATE   AVAILABLE\n# deployment.apps\u002Fppai-backend     1\u002F1     1            1\n# deployment.apps\u002Fppai-frontend    2\u002F2     2            2\n#\n# NAME                               READY   AGE\n# statefulset.apps\u002Fppai-database     1\u002F1     ...\n\n# Ver el Ingress y su IP asignada\nkubectl get ingress -n ppai\n\n# Ver estado del autoscaling\nkubectl get hpa -n ppai\n\n# Verificar el health del backend\nkubectl exec -n ppai deploy\u002Fppai-backend -- \\\n  wget -qO- http:\u002F\u002Flocalhost:3001\u002Fapi\u002Fhealth\n```\n\n---\n\n## Paso 7 — Cargar datos iniciales (seed)\n\nSolo necesario la **primera vez** en un cluster nuevo, o después de un restore en blanco.\nCrea las cuentas de prueba: `admin@app.com`, `editor@app.com`, `viewer@app.com` (password: `*123`).\n\n```bash\n# Aplicar el job\nkubectl apply -f k8s\u002F18-seed-job.yaml\n\n# Seguir el progreso\nkubectl logs -n ppai -l job-name=ppai-seed -f\n\n# Limpiar el job cuando termine\nkubectl delete -f k8s\u002F18-seed-job.yaml\n```\n\n---\n\n## TLS \u002F HTTPS con Let's Encrypt\n\n### 1. Crear el ClusterIssuer\n\n```yaml\n# cluster-issuer.yaml\napiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt-prod\nspec:\n  acme:\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory\n    email: tu@email.com          # ← tu email real\n    privateKeySecretRef:\n      name: letsencrypt-prod\n    solvers:\n      - http01:\n          ingress:\n            class: nginx\n```\n\n```bash\nkubectl apply -f cluster-issuer.yaml\n```\n\n### 2. Habilitar TLS en el Ingress\n\nEditar `k8s\u002F12-ingress.yaml` y descomentar\u002Fagregar las secciones TLS:\n\n```yaml\nmetadata:\n  annotations:\n    nginx.ingress.kubernetes.io\u002Fssl-redirect: \"true\"\n    cert-manager.io\u002Fcluster-issuer: \"letsencrypt-prod\"   # ← descomentar\n\nspec:\n  tls:                                                    # ← descomentar bloque\n    - hosts:\n        - ppai.midominio.com\n      secretName: ppai-tls-cert\n\n  rules:\n    - host: ppai.midominio.com\n```\n\n```bash\nkubectl apply -f k8s\u002F12-ingress.yaml\n\n# Verificar que el certificado se emitió (puede tardar 1-2 min)\nkubectl get certificate -n ppai\nkubectl describe certificate ppai-tls-cert -n ppai\n```\n\n---\n\n## Backup y Restore de la base de datos\n\n### Backup automático\n\nEl CronJob `ppai-backup` ejecuta `pg_dump` todos los días a las **02:00 hora de Bogotá**.\nLos backups se guardan en el PVC `ppai-backups-pvc` con el formato `ppai_YYYYMMDD_HHMMSS.dump`\ny se purgan automáticamente después de 30 días.\n\n**Ejecutar un backup manual ahora:**\n```bash\nkubectl create job --from=cronjob\u002Fppai-backup ppai-backup-manual -n ppai\n\n# Ver el progreso\nkubectl logs -n ppai -l job-name=ppai-backup-manual -f\n```\n\n### Listar backups disponibles\n\n```bash\nkubectl run -n ppai list-backups --rm -it --restart=Never \\\n  --image=alpine \\\n  --overrides='{\n    \"spec\": {\n      \"volumes\": [{\"name\":\"b\",\"persistentVolumeClaim\":{\"claimName\":\"ppai-backups-pvc\"}}],\n      \"containers\": [{\n        \"name\":\"c\", \"image\":\"alpine\",\n        \"command\":[\"ls\",\"-lht\",\"\u002Fbackups\"],\n        \"volumeMounts\":[{\"name\":\"b\",\"mountPath\":\"\u002Fbackups\"}]\n      }]\n    }\n  }'\n```\n\n### Restaurar desde un backup\n\n```bash\n# 1. Escalar el backend a 0 para evitar escrituras durante el restore\nkubectl scale deployment ppai-backend -n ppai --replicas=0\n\n# 2. Editar el nombre del archivo en el job de restore\n#    Cambiar la línea: value: \"ppai_YYYYMMDD_HHMMSS.dump\"\nnano k8s\u002F17-restore-job.yaml\n\n# 3. Aplicar el job\nkubectl apply -f k8s\u002F17-restore-job.yaml\n\n# 4. Seguir el progreso\nkubectl logs -n ppai -l job-name=ppai-restore -f\n\n# 5. Restaurar el backend cuando el restore termine\nkubectl scale deployment ppai-backend -n ppai --replicas=1\n\n# 6. Limpiar el job\nkubectl delete -f k8s\u002F17-restore-job.yaml\n```\n\n---\n\n## Actualizar la aplicación\n\nCuando se publica una nueva imagen en Docker Hub:\n\n```bash\n# Forzar re-descarga de la imagen (sin cambiar los YAMLs)\nkubectl rollout restart deployment\u002Fppai-backend  -n ppai\nkubectl rollout restart deployment\u002Fppai-frontend -n ppai\n\n# Verificar que el rollout completó sin errores\nkubectl rollout status deployment\u002Fppai-backend  -n ppai\nkubectl rollout status deployment\u002Fppai-frontend -n ppai\n```\n\nPara hacer rollback a la versión anterior:\n```bash\nkubectl rollout undo deployment\u002Fppai-backend  -n ppai\nkubectl rollout undo deployment\u002Fppai-frontend -n ppai\n```\n\n---\n\n## Referencia de imágenes Docker Hub\n\n| Servicio | Imagen |\n|---|---|\n| Backend | `REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-backend:latest` |\n| Frontend | `REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-frontend:latest` |\n\nLas imágenes se construyen desde este repositorio con:\n\n```bash\n# Construir\ndocker compose build\n\n# Publicar en Docker Hub\ndocker tag pp-ai-deploy-backend  REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-backend:latest\ndocker tag pp-ai-deploy-frontend REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-frontend:latest\ndocker push REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-backend:latest\ndocker push REGISTRY_URL\u002FREGISTRY_PROJECT\u002Fpp-ai-frontend:latest\n```\n\n---\n\n## Referencia de manifests\n\n| Archivo | Recurso K8s | Descripción |\n|---|---|---|\n| `01-namespace.yaml` | Namespace | Namespace `ppai` que agrupa todos los recursos |\n| `02-secrets.yaml` | Secret | Passwords y tokens (editar antes de aplicar) |\n| `03-configmap.yaml` | ConfigMap | Variables no-sensibles: hosts, puertos, dominio |\n| `04-database-statefulset.yaml` | StatefulSet | PostgreSQL 16 con volumen persistente |\n| `05-database-service.yaml` | Service (headless) | DNS estable para el StatefulSet de Postgres |\n| `06-backend-pvc.yaml` | PVC | Volumen 10 Gi para `\u002Fapp\u002Fuploads` |\n| `07-backend-deployment.yaml` | Deployment | API Node.js — espera a Postgres vía initContainer |\n| `08-backend-service.yaml` | Service (ClusterIP) | Expone el backend en `:3001` dentro del cluster |\n| `09-frontend-configmap-nginx.yaml` | ConfigMap | nginx.conf con proxy al nombre de servicio K8s |\n| `10-frontend-deployment.yaml` | Deployment | Frontend Nginx, 2 réplicas, monta el ConfigMap |\n| `11-frontend-service.yaml` | Service (ClusterIP) | Expone el frontend en `:80` dentro del cluster |\n| `12-ingress.yaml` | Ingress | Punto de entrada externo con rate-limit y CORS |\n| `13-hpa.yaml` | HorizontalPodAutoscaler | Escala el frontend entre 2 y 6 réplicas por CPU\u002FRAM |\n| `14-networkpolicy.yaml` | NetworkPolicy | Solo backend habla con Postgres; solo Ingress habla con frontend |\n| `15-backup-pvc.yaml` | PVC | Volumen 20 Gi para almacenar dumps de Postgres |\n| `16-backup-cronjob.yaml` | CronJob | `pg_dump` diario, retiene 30 días de historial |\n| `17-restore-job.yaml` | Job | Restore manual — editar `BACKUP_FILE` y aplicar |\n| `18-seed-job.yaml` | Job | Carga datos de ejemplo (solo primer deploy) |\n\n---\n\n## Troubleshooting\n\n### Los pods no arrancan\n\n```bash\n# Ver eventos recientes del namespace\nkubectl get events -n ppai --sort-by=.lastTimestamp | tail -20\n\n# Describir un pod con problemas\nkubectl describe pod \u003Cnombre-del-pod> -n ppai\n```\n\n### El backend falla con \"environment variable is not set\"\n\nEl backend hace `process.exit(1)` si falta `JWT_SECRET`, `DB_HOST`, `DB_NAME`, `DB_USER`\no `DB_PASSWORD`. Verificar que el Secret y el ConfigMap se aplicaron correctamente:\n\n```bash\nkubectl get secret ppai-secrets   -n ppai\nkubectl get configmap ppai-config -n ppai\n```\n\n### No se puede acceder a la aplicación por el dominio\n\n```bash\n# Verificar que el Ingress tiene IP asignada\nkubectl get ingress -n ppai\n\n# Verificar que el DNS apunta a esa IP\nnslookup ppai.midominio.com\n\n# Probar directamente por IP (saltando DNS)\ncurl -H \"Host: ppai.midominio.com\" http:\u002F\u002F\u003CEXTERNAL-IP>\u002Fapi\u002Fhealth\n```\n\n### Ver logs en tiempo real\n\n```bash\nkubectl logs -n ppai -l app.kubernetes.io\u002Fname=ppai-backend  -f\nkubectl logs -n ppai -l app.kubernetes.io\u002Fname=ppai-frontend -f\nkubectl logs -n ppai -l app.kubernetes.io\u002Fname=ppai-database -f\n```\n\n### Conectarse directamente a la base de datos\n\n```bash\nkubectl exec -n ppai -it statefulset\u002Fppai-database -- psql -U ppai -d ppai\n```\n\n### Eliminar todo el despliegue\n\n```bash\n# ⚠ Esto elimina todos los recursos incluyendo los datos persistentes\nkubectl delete namespace ppai\n```\n","PP-AI 是一款基于人工智能的项目管理工具。该项目使用 TypeScript 编写，通过 Docker 和 Kubernetes 进行部署和管理，支持快速启动与配置，并提供了包括前端、后端以及数据库在内的完整开发环境。其核心功能在于利用 AI 技术优化项目管理工作流，提高团队协作效率。特别适合需要高效项目管理和跨部门协作的企业或团队使用，尤其是在对自动化需求较高的软件开发场景中。此外，PP-AI 也适用于希望探索AI在日常业务流程中应用潜力的技术爱好者或小规模项目团队。","2026-06-11 04:04:39","CREATED_QUERY"]