diff --git a/.gitea-ci.yml b/.gitea-ci.yml index 45e7c15..4399050 100644 --- a/.gitea-ci.yml +++ b/.gitea-ci.yml @@ -56,7 +56,7 @@ jobs: - name: Create secrets file run: | - cat > ./chart/secrets.prod.yml << EOF + cat > ./chart/secrets.prod.yaml << EOF backend: env: POSTGRES_PASSWORD: "${{ secrets.POSTGRES_PASSWORD }}" @@ -72,8 +72,8 @@ jobs: helm upgrade --install fusero ./chart \ --namespace fusero-prod \ --create-namespace \ - --values ./chart/values.prod.yml \ - --values ./chart/secrets.prod.yml \ + --values ./chart/values.prod.yaml \ + --values ./chart/secrets.prod.yaml \ --set backend.image.repository=registry.liquidrinu.com/fusero-backend \ --set frontend.image.repository=registry.liquidrinu.com/fusero-frontend diff --git a/.gitignore b/.gitignore index be7a341..0e0326d 100644 --- a/.gitignore +++ b/.gitignore @@ -125,20 +125,20 @@ values.dev.* values.prod.* # Secrets -chart/secrets.prod.yml -chart/secrets.*.yml +chart/secrets.prod.yaml +chart/secrets.*.yaml *.env .env.* # Development values with secrets -chart/values.dev.yml +chart/values.dev.yaml # Production secrets -chart/secrets.prod.yml -chart/secrets.*.yml +chart/secrets.prod.yaml +chart/secrets.*.yaml # Keep templates and public configs -!chart/secrets.prod.template.yml -!chart/values.prod.template.yml -!chart/values.prod.public.yml +!chart/secrets.prod.template.yaml +!chart/values.prod.template.yaml +!chart/values.prod.public.yaml diff --git a/README.md b/README.md index d378443..01049ed 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A full-stack application boilerplate with a React frontend and Node.js backend - [To seed the database:](#to-seed-the-database) - [Alternate: Running Services in Separate Terminals](#alternate-running-services-in-separate-terminals) - [🛠️ Environment Setup](#️-environment-setup) -- [For Kubernetes, these are set in chart/values.yml:](#for-kubernetes-these-are-set-in-chartvaluesyml) +- [For Kubernetes, these are set in chart/values.yaml:](#for-kubernetes-these-are-set-in-chartvaluesyaml) - [POSTGRES\_NAME=fusero-boilerplate-db](#postgres_namefusero-boilerplate-db) - [POSTGRES\_HOSTNAME=postgres-service](#postgres_hostnamepostgres-service) - [POSTGRES\_PORT=19095](#postgres_port19095) @@ -53,6 +53,7 @@ A full-stack application boilerplate with a React frontend and Node.js backend - [Port-Forwarding for Local Access](#port-forwarding-for-local-access) - [Frontend (React app)](#frontend-react-app) - [Backend (API)](#backend-api) + - [Database](#database) - [NGINX Backend Service Name: Docker Compose vs Kubernetes](#nginx-backend-service-name-docker-compose-vs-kubernetes) - [How to update the NGINX config for Kubernetes](#how-to-update-the-nginx-config-for-kubernetes) - [Cleaning Up Duplicate or Crashing Deployments and Pods in Kubernetes](#cleaning-up-duplicate-or-crashing-deployments-and-pods-in-kubernetes) @@ -79,6 +80,7 @@ A full-stack application boilerplate with a React frontend and Node.js backend - [Database Backup](#database-backup) - [CI/CD \& Automated Testing](#cicd--automated-testing) - [Troubleshooting Production](#troubleshooting-production) + - [🆕 Recent Improvements \& Troubleshooting](#-recent-improvements--troubleshooting) --- @@ -88,7 +90,7 @@ fusero-app-boilerplate/ ├── chart/ # Helm chart for Kubernetes │ ├── Chart.yaml │ ├── values.dev.yaml -│ ├── values.prod.yml +│ ├── values.prod.yaml │ └── templates/ ├── config/ ├── coverage/ @@ -102,10 +104,10 @@ fusero-app-boilerplate/ ├── node_modules/ ├── package.json ├── package-lock.json -├── docker-compose.yml -├── docker-compose.dev.yml +├── docker-compose.yaml +├── docker-compose.dev.yaml ├── .gitignore -├── .gitea-ci.yml +├── .gitea-ci.yaml ├── .prettierrc.json ├── .eslintrc.json ├── architecture.excalidraw @@ -192,7 +194,7 @@ POSTGRES_USER=root POSTGRES_PASSWORD=root123 JWT_SECRET=your_jwt_secret_key_here -# For Kubernetes, these are set in chart/values.yml: +# For Kubernetes, these are set in chart/values.yaml: # POSTGRES_NAME=fusero-boilerplate-db # POSTGRES_HOSTNAME=postgres-service # POSTGRES_PORT=19095 @@ -248,7 +250,6 @@ docker exec -it fusero-app-backend npm run migrate docker exec -it fusero-app-backend npm run seed 3. Ensure all required environment variables are configured. -Never commit `.env` files. --- @@ -275,7 +276,7 @@ Wrong: to="/dashboard/canvas/canvas-endpoints" Generate a self-signed cert: openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./nginx/ssl/nginx.key -out ./nginx/ssl/nginx.crt -Ensure `docker-compose.yml` mounts the certs: +Ensure `docker-compose.yaml` mounts the certs: volumes: - ./nginx/ssl:/etc/nginx/ssl @@ -286,7 +287,7 @@ Configure NGINX to use the cert in production. ## 🧠 Development Best Practices - Always run the DB via Docker -- Use `docker-compose.dev.yml` for development +- Use `docker-compose.dev.yaml` for development - Never run PostgreSQL directly on host - Run frontend and backend separately for hot reload - Use `.env.example` as a template @@ -341,8 +342,8 @@ lsof -i :14000 Database Issues: Ensure DB is in Docker and configured correctly Try restarting: -docker-compose -f docker-compose.dev.yml down -docker-compose -f docker-compose.dev.yml up db +docker-compose -f docker-compose.dev.yaml down +docker-compose -f docker-compose.dev.yaml up db CORS Issues: Check API base URL in frontend `.env` @@ -384,7 +385,7 @@ docker push /fusero-backend-dev:local ### 3. Upgrade the Helm release with the latest values ```bash -helm upgrade fusero ./chart -n fusero -f chart/values.dev.yml +helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml ``` ### 4. Restart the backend deployment to pick up new images and env vars @@ -430,7 +431,7 @@ docker push /fusero-frontend-dev:local ### 3. Upgrade the Helm release ```bash -helm upgrade fusero ./chart -n fusero -f chart/values.dev.yml +helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml ``` ### 4. Restart the frontend deployment @@ -446,16 +447,22 @@ To access your services running in Kubernetes from your local machine, use these ### Frontend (React app) ```bash -kubectl port-forward -n fusero svc/fusero-frontend-service 3000:80 +kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80 ``` - Access at: http://localhost:3000 ### Backend (API) ```bash -kubectl port-forward -n fusero svc/fusero-backend-service 14000:14000 +kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000 ``` - Access at: http://localhost:14000 +### Database +```bash +kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432 +``` +- Access at: localhost:5432 + --- ## NGINX Backend Service Name: Docker Compose vs Kubernetes @@ -482,7 +489,7 @@ Then rebuild and redeploy the frontend: ```bash docker build -t fusero-frontend-dev:local ./frontend # (push if needed) -helm upgrade fusero ./chart -n fusero -f chart/values.dev.yml +helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml kubectl rollout restart deployment/fusero-frontend -n fusero ``` @@ -546,7 +553,7 @@ This means NGINX is trying to load an SSL certificate that does not exist in the 3. (If using a remote registry) Push the image. 4. Redeploy with Helm: ```bash - helm upgrade fusero ./chart -n fusero -f chart/values.dev.yml + helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml ``` 5. Check pod status: ```bash @@ -592,7 +599,7 @@ kubectl create namespace fusero-dev kubectl config set-context --current --namespace=fusero-dev # Deploy to development namespace -helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yml +helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml ``` ### Production Namespace Setup @@ -604,7 +611,7 @@ kubectl create namespace fusero-prod kubectl config set-context --current --namespace=fusero-prod # Deploy to production namespace -helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yml +helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml ``` ### Namespace Management Commands @@ -656,8 +663,8 @@ kubectl delete namespace You have configured your Kubernetes development environment to use a dedicated namespace (`fusero-dev`) for all core services (backend, frontend, and database). This ensures resource isolation and easier management between development and production. ### What was changed: -- All Kubernetes service templates (`backend-service.yml`, `frontend-service.yml`, `postgres-service.yml`) now use a namespace variable. -- The development values file (`chart/values.dev.yml`) sets `global.namespace: fusero-dev` and updates all internal service hostnames to use this namespace. +- All Kubernetes service templates (`backend-service.yaml`, `frontend-service.yaml`, `postgres-service.yaml`) now use a namespace variable. +- The development values file (`chart/values.dev.yaml`) sets `global.namespace: fusero-dev` and updates all internal service hostnames to use this namespace. - Helm deployments now target the `fusero-dev` namespace for all dev resources. ### How to use: @@ -668,7 +675,7 @@ You have configured your Kubernetes development environment to use a dedicated n 2. **Deploy all services to the namespace:** ```bash - helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yml + helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml ``` 3. **Check running pods and services:** @@ -681,7 +688,7 @@ You have configured your Kubernetes development environment to use a dedicated n 5. **If you see errors about immutable fields (e.g., for Jobs), delete the old Job before redeploying:** ```bash kubectl delete job -n fusero-dev - helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yml + helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml ``` ### Why use namespaces? @@ -703,7 +710,7 @@ The application uses a secure secrets management approach: 2. **Production Environment**: - Secrets are managed through Gitea CI/CD secrets - - Template file: `chart/secrets.prod.template.yml` + - Template file: `chart/secrets.prod.template.yaml` - Actual secrets are generated during deployment - Never commit actual secrets to the repository @@ -716,7 +723,7 @@ The application uses a secure secrets management approach: 4. **Secrets in CI/CD**: - Secrets are stored in Gitea CI/CD settings - Automatically injected during deployment - - Used to generate `secrets.prod.yml` at runtime + - Used to generate `secrets.prod.yaml` at runtime 5. **Security Best Practices**: - All secrets files are gitignored @@ -757,7 +764,7 @@ The application uses a secure secrets management approach: - name: Run Tests run: npm test - name: Deploy to Kubernetes - run: helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yml + run: helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml ``` ### Troubleshooting Production @@ -767,3 +774,50 @@ The application uses a secure secrets management approach: - Rollback: Use `helm rollback fusero -n fusero-prod`. --- + +## 🆕 Recent Improvements & Troubleshooting + +- **Consistent File Extensions:** All Kubernetes and Helm YAML files now use the `.yaml` extension for consistency. +- **Secrets Management:** + - Development secrets are stored in `chart/secrets.dev.yaml` (gitignored). + - Production secrets are generated by CI/CD as `chart/secrets.prod.yaml` from Gitea secrets. + - All values files (`values.dev.yaml`, `values.prod.yaml`) reference secrets via environment variables. +- **CORS Configuration:** + - For local development, set `CORS_ORIGIN: "http://localhost:3000"` in `chart/values.dev.yaml` to allow credentialed requests from the frontend. + - Do **not** use `*` for CORS origin if you need credentials. +- **Kubernetes Job Immutability:** + - If you update environment variables or secrets, you must delete the old migration/seed job before redeploying: + ```bash + kubectl delete job fusero-backend-db-init -n fusero-dev + helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml -f chart/secrets.dev.yaml + ``` +- **Port Forwarding for Local Access:** + - Backend: + ```bash + kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000 + ``` + - Frontend: + ```bash + kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80 + ``` + - Database: + ```bash + kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432 + ``` +- **Testing Login with curl:** + ```bash + curl -i -X POST http://localhost:14000/api/v1/app/users/login \ + -H "Content-Type: application/json" \ + -H "Origin: http://localhost:3000" \ + -d '{"username":"admin","password":"admin123"}' + ``` +- **Troubleshooting 401 Errors:** + - If login fails after a redeploy: + - Ensure secrets and values are in sync. + - Re-run the seed job as above. + - Check backend logs for authentication errors: + ```bash + kubectl logs -n fusero-dev -l app=fusero-backend --tail=100 + ``` + +--- diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..22f713b --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: fusero +description: Fusero App Boilerplate Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/chart/Chart.yml b/chart/Chart.yml index 3708c76..22f713b 100644 --- a/chart/Chart.yml +++ b/chart/Chart.yml @@ -1,6 +1,6 @@ apiVersion: v2 name: fusero -description: Fusero application Helm chart +description: Fusero App Boilerplate Helm Chart type: application version: 0.1.0 appVersion: "1.0.0" diff --git a/chart/secrets.prod.template.yml b/chart/secrets.prod.template.yml deleted file mode 100644 index 1ecd2c3..0000000 --- a/chart/secrets.prod.template.yml +++ /dev/null @@ -1,26 +0,0 @@ -backend: - env: - # Database - POSTGRES_NAME: "fusero-boilerplate-db" - POSTGRES_HOSTNAME: "postgres-service.fusero-prod.svc.cluster.local" - POSTGRES_PORT: "5432" - POSTGRES_USER: "root" - POSTGRES_PASSWORD: "REPLACE_ME" - - # Admin User - DEFAULT_ADMIN_USERNAME: "admin" - DEFAULT_ADMIN_EMAIL: "admin@fusero.nl" - DEFAULT_ADMIN_PASSWORD: "REPLACE_ME" - - # Security - ENCRYPTION_KEY: "REPLACE_ME" - JWT_SECRET: "REPLACE_ME" - - # API Keys - CHATGPT_API_KEY: "REPLACE_ME" - CANVAS_API_KEY: "REPLACE_ME" - CANVAS_API_URL: "https://talnet.instructure.com/api/v1" - - # Server - FASTIFY_PORT: "14000" - NODE_ENV: "production" diff --git a/chart/templates/backend-deployment.yml b/chart/templates/backend-deployment.yaml similarity index 100% rename from chart/templates/backend-deployment.yml rename to chart/templates/backend-deployment.yaml diff --git a/chart/templates/backend-migration.job.yml b/chart/templates/backend-migration.job.yaml similarity index 100% rename from chart/templates/backend-migration.job.yml rename to chart/templates/backend-migration.job.yaml diff --git a/chart/templates/backend-secrets.yml b/chart/templates/backend-secrets.yaml similarity index 100% rename from chart/templates/backend-secrets.yml rename to chart/templates/backend-secrets.yaml diff --git a/chart/templates/backend-service.yml b/chart/templates/backend-service.yaml similarity index 100% rename from chart/templates/backend-service.yml rename to chart/templates/backend-service.yaml diff --git a/chart/templates/frontend-deployment.yml b/chart/templates/frontend-deployment.yaml similarity index 100% rename from chart/templates/frontend-deployment.yml rename to chart/templates/frontend-deployment.yaml diff --git a/chart/templates/frontend-service.yml b/chart/templates/frontend-service.yaml similarity index 100% rename from chart/templates/frontend-service.yml rename to chart/templates/frontend-service.yaml diff --git a/chart/templates/postgres-configmap.yaml b/chart/templates/postgres-configmap.yaml new file mode 100644 index 0000000..e2452f6 --- /dev/null +++ b/chart/templates/postgres-configmap.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres-config +data: + pg_hba.conf: | + # TYPE DATABASE USER ADDRESS METHOD + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + host all all 0.0.0.0/0 md5 + postgresql.conf: | + listen_addresses = '*' + max_connections = 100 + shared_buffers = 128MB + dynamic_shared_memory_type = posix + max_wal_size = 1GB + min_wal_size = 80MB \ No newline at end of file diff --git a/chart/templates/postgres-deployment.yml b/chart/templates/postgres-deployment.yaml similarity index 71% rename from chart/templates/postgres-deployment.yml rename to chart/templates/postgres-deployment.yaml index af643e2..fceff3b 100644 --- a/chart/templates/postgres-deployment.yml +++ b/chart/templates/postgres-deployment.yaml @@ -27,7 +27,16 @@ spec: volumeMounts: - mountPath: /var/lib/postgresql/data name: postgres-data + - mountPath: /etc/postgresql/pg_hba.conf + name: postgres-config + subPath: pg_hba.conf + - mountPath: /etc/postgresql/postgresql.conf + name: postgres-config + subPath: postgresql.conf volumes: - name: postgres-data persistentVolumeClaim: claimName: postgres-pvc-fresh + - name: postgres-config + configMap: + name: postgres-config diff --git a/chart/templates/postgres-pvc.yml b/chart/templates/postgres-pvc.yaml similarity index 100% rename from chart/templates/postgres-pvc.yml rename to chart/templates/postgres-pvc.yaml diff --git a/chart/templates/postgres-service.yml b/chart/templates/postgres-service.yaml similarity index 100% rename from chart/templates/postgres-service.yml rename to chart/templates/postgres-service.yaml diff --git a/chart/values.prod.public.yml b/chart/values.prod.public.yaml similarity index 100% rename from chart/values.prod.public.yml rename to chart/values.prod.public.yaml diff --git a/chart/values.prod.yml b/chart/values.prod.yml deleted file mode 100644 index 00879e2..0000000 --- a/chart/values.prod.yml +++ /dev/null @@ -1,64 +0,0 @@ -global: - namespace: fusero-prod - security: - cors: - origin: "https://app.fusero.nl" - methods: "GET,POST,PUT,DELETE" - credentials: true - https: - enabled: true - certSecret: "fusero-tls-secret" - logging: - level: "info" - format: "json" - monitoring: - prometheus: - enabled: true - path: "/metrics" - -backend: - image: registry.liquidrinu.com/fusero-backend:latest - resources: - requests: - cpu: "200m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "512Mi" - env: - POSTGRES_HOST: postgres-service - POSTGRES_PORT: "5432" - POSTGRES_NAME: fusero-db - POSTGRES_USER: prod_admin - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - DEFAULT_ADMIN_USERNAME: admin - DEFAULT_ADMIN_EMAIL: admin@fusero.nl - DEFAULT_ADMIN_PASSWORD: ${DEFAULT_ADMIN_PASSWORD} - ENCRYPTION_KEY: ${ENCRYPTION_KEY} - JWT_SECRET: ${JWT_SECRET} - CHATGPT_API_KEY: ${CHATGPT_API_KEY} - CANVAS_API_KEY: ${CANVAS_API_KEY} - CANVAS_API_URL: https://talnet.instructure.com/api/v1 - -frontend: - image: registry.liquidrinu.com/fusero-frontend:latest - resources: - requests: - cpu: "100m" - memory: "128Mi" - limits: - cpu: "300m" - memory: "256Mi" - env: - VITE_API_BASE_URL: https://app.fusero.nl - -postgres: - image: postgres:15 - storage: 5Gi - resources: - requests: - cpu: "200m" - memory: "256Mi" - limits: - cpu: "500m" - memory: "512Mi" diff --git a/package.json b/package.json index 9095dd2..d923049 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "migrate": "npx mikro-orm migration:up", "seed": "ts-node -r tsconfig-paths/register src/database/seeds/run-seed.ts", "app:generate": "node ./utils/generate-app.js", - "k8s:dev:apply": "kubectl apply -f k8s/frontend/deployment.dev.yml", + "k8s:dev:apply": "kubectl apply -f k8s/frontend/deployment.dev.yaml", "k8s:dev:delete": "kubectl delete deployment fusero-frontend-dev 2>/dev/null || true", "k8s:dev:run": "kubectl port-forward svc/fusero-frontend-service 3000:80", "k8s:dev:describe": "kubectl describe pod -l app=fusero-frontend",