mega update: k8s backend, frontend, database, pods, helm charts, k8s, load balancing, gitea-ci.yml explanded readme
This commit is contained in:
parent
b48b3dc027
commit
ce4371d9e4
12
.gitea-ci.yml
Normal file
12
.gitea-ci.yml
Normal file
@ -0,0 +1,12 @@
|
||||
image: lachlanevenson/k8s-helm:latest
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
KUBECONFIG: /root/.kube/config # Adjust if you're mounting a kubeconfig differently
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- helm upgrade --install fusero ./chart -f ./chart/values-prod.yaml
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -114,4 +114,13 @@ test/types/index.js
|
||||
dist
|
||||
|
||||
# general
|
||||
docs
|
||||
bkups
|
||||
|
||||
# k8s
|
||||
secrets.yml
|
||||
|
||||
# helm
|
||||
values.dev.*
|
||||
values.prod.*
|
||||
|
||||
|
@ -6,5 +6,6 @@
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 144,
|
||||
"bracketSpacing": true
|
||||
"bracketSpacing": true,
|
||||
"plugins": ["@helm/prettier-plugin-helm"]
|
||||
}
|
||||
|
697
README.md
697
README.md
@ -1,269 +1,486 @@
|
||||
# Fusero App Boilerplate
|
||||
# ⚡️ Fusero App Boilerplate
|
||||
|
||||
A full-stack application boilerplate with React frontend and Node.js backend.
|
||||
A full-stack application boilerplate with a React frontend and Node.js backend — powered by Fastify, Vite, PostgreSQL, Docker, and optional Kubernetes & Helm support. Built for modern dev workflows and AI-powered backend endpoint generation.
|
||||
|
||||
## Project Structure
|
||||
---
|
||||
|
||||
## 📚 Table of Contents
|
||||
|
||||
- [⚡️ Fusero App Boilerplate](#️-fusero-app-boilerplate)
|
||||
- [📚 Table of Contents](#-table-of-contents)
|
||||
- [📁 Project Structure](#-project-structure)
|
||||
- [⚙️ Prerequisites](#️-prerequisites)
|
||||
- [💻 Development Setup](#-development-setup)
|
||||
- [Alternate: Running Services in Separate Terminals](#alternate-running-services-in-separate-terminals)
|
||||
- [🛠️ Environment Setup](#️-environment-setup)
|
||||
- [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)
|
||||
- [POSTGRES\_USER=root](#postgres_userroot)
|
||||
- [POSTGRES\_PASSWORD=root123](#postgres_passwordroot123)
|
||||
- [🚀 Production Deployment](#-production-deployment)
|
||||
- [🌐 Frontend Routing in Production](#-frontend-routing-in-production)
|
||||
- [🔐 HTTPS with Self-Signed Certificates](#-https-with-self-signed-certificates)
|
||||
- [🧠 Development Best Practices](#-development-best-practices)
|
||||
- [📘 API Documentation](#-api-documentation)
|
||||
- [🧩 ChatGPT-Powered Endpoint Creation](#-chatgpt-powered-endpoint-creation)
|
||||
- [🧪 Troubleshooting](#-troubleshooting)
|
||||
- [🤝 Contributing](#-contributing)
|
||||
- [📄 License](#-license)
|
||||
- [Kubernetes Troubleshooting \& Redeployment Commands](#kubernetes-troubleshooting--redeployment-commands)
|
||||
- [1. Rebuild the backend Docker image (after code/config changes)](#1-rebuild-the-backend-docker-image-after-codeconfig-changes)
|
||||
- [2. (If using a remote registry) Push the image](#2-if-using-a-remote-registry-push-the-image)
|
||||
- [3. Upgrade the Helm release with the latest values](#3-upgrade-the-helm-release-with-the-latest-values)
|
||||
- [4. Restart the backend deployment to pick up new images and env vars](#4-restart-the-backend-deployment-to-pick-up-new-images-and-env-vars)
|
||||
- [5. Check backend pod environment variables](#5-check-backend-pod-environment-variables)
|
||||
- [6. Check backend pod logs for errors](#6-check-backend-pod-logs-for-errors)
|
||||
- [7. If you change DB env vars or code, repeat steps 1-6](#7-if-you-change-db-env-vars-or-code-repeat-steps-1-6)
|
||||
- [Frontend Rebuild \& Redeploy (Kubernetes)](#frontend-rebuild--redeploy-kubernetes)
|
||||
- [1. Rebuild the frontend Docker image](#1-rebuild-the-frontend-docker-image)
|
||||
- [2. (If using a remote registry) Push the image](#2-if-using-a-remote-registry-push-the-image-1)
|
||||
- [3. Upgrade the Helm release](#3-upgrade-the-helm-release)
|
||||
- [4. Restart the frontend deployment](#4-restart-the-frontend-deployment)
|
||||
- [Port-Forwarding for Local Access](#port-forwarding-for-local-access)
|
||||
- [Frontend (React app)](#frontend-react-app)
|
||||
- [Backend (API)](#backend-api)
|
||||
- [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)
|
||||
- [1. List deployments and pods](#1-list-deployments-and-pods)
|
||||
- [2. Delete old or crashing deployments (example IDs from your cluster)](#2-delete-old-or-crashing-deployments-example-ids-from-your-cluster)
|
||||
- [3. Delete old or crashing pods (example IDs from your cluster)](#3-delete-old-or-crashing-pods-example-ids-from-your-cluster)
|
||||
- [Debugging Frontend Pod Crashes: NGINX SSL Certificate Errors](#debugging-frontend-pod-crashes-nginx-ssl-certificate-errors)
|
||||
- [How to fix for Kubernetes (Recommended)](#how-to-fix-for-kubernetes-recommended)
|
||||
- [Connecting to the Database from Your Host (DBeaver, etc.)](#connecting-to-the-database-from-your-host-dbeaver-etc)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
fusero-app-boilerplate/
|
||||
├── frontend/ # React frontend application
|
||||
├── backend/ # Node.js backend application
|
||||
├── docker-compose.yml # Production Docker configuration
|
||||
└── docker-compose.dev.yml # Development Docker configuration
|
||||
```
|
||||
├── docker-compose.dev.yml # Development Docker configuration
|
||||
└── chart/ # Helm chart for Kubernetes deployment
|
||||
|
||||
## Prerequisites
|
||||
---
|
||||
|
||||
## ⚙️ Prerequisites
|
||||
|
||||
- Node.js (v20 or higher)
|
||||
- npm (v9 or higher)
|
||||
- Docker and Docker Compose
|
||||
- Docker & Docker Compose
|
||||
- Git
|
||||
|
||||
## Development Setup
|
||||
---
|
||||
|
||||
### Important Note: Database Must Run in Docker
|
||||
The PostgreSQL database must always run in Docker, regardless of your development setup choice. This ensures consistent database behavior across all environments.
|
||||
## 💻 Development Setup
|
||||
|
||||
To start the database:
|
||||
```bash
|
||||
docker-compose up db
|
||||
```
|
||||
🗃️ PostgreSQL must run in Docker for consistent behavior.
|
||||
|
||||
### Running Services Separately (Recommended for Development)
|
||||
Create volume and start the database:
|
||||
docker volume create fusero-db-data
|
||||
docker-compose up -d db
|
||||
|
||||
For better debugging experience, run the frontend and backend in separate terminal windows, while keeping the database in Docker:
|
||||
Backend setup:
|
||||
cd backend
|
||||
cp .env.example .env
|
||||
npm install
|
||||
npm run migrate
|
||||
npm run seed
|
||||
npm run dev &
|
||||
cd ..
|
||||
|
||||
1. **First, ensure the database is running in Docker**
|
||||
```bash
|
||||
docker-compose up db
|
||||
```
|
||||
Frontend setup:
|
||||
cd frontend
|
||||
cp .env.example .env
|
||||
npm install
|
||||
npm run dev &
|
||||
cd ..
|
||||
|
||||
2. **Then, in separate terminal windows:**
|
||||
App is running:
|
||||
Frontend → http://localhost:3000
|
||||
Backend → http://localhost:14000
|
||||
|
||||
#### Terminal 1: Backend Service
|
||||
```bash
|
||||
---
|
||||
|
||||
### Alternate: Running Services in Separate Terminals
|
||||
|
||||
Terminal 1 (backend):
|
||||
cd backend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
The backend will be available at http://localhost:14000
|
||||
|
||||
#### Terminal 2: Frontend Service
|
||||
```bash
|
||||
Terminal 2 (frontend):
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
The frontend will be available at http://localhost:3000
|
||||
|
||||
### Database Setup
|
||||
|
||||
1. **Create a New Volume**
|
||||
- Ensure the database volume is created:
|
||||
```bash
|
||||
docker volume create fusero-db-data
|
||||
```
|
||||
|
||||
2. **Run Migrations**
|
||||
- Apply database migrations to set up the schema:
|
||||
```bash
|
||||
cd backend
|
||||
npm run migrate
|
||||
```
|
||||
|
||||
3. **Seed the Database**
|
||||
- Populate the database with initial data:
|
||||
```bash
|
||||
cd backend
|
||||
npm run seed
|
||||
```
|
||||
|
||||
### Environment Setup
|
||||
|
||||
1. **Backend Environment**
|
||||
- Copy `.env.example` to `.env` in the backend directory
|
||||
- Configure your environment variables:
|
||||
```
|
||||
PORT=14000
|
||||
DB_HOST=localhost
|
||||
DB_PORT=19090
|
||||
DB_USER=postgres
|
||||
DB_PASSWORD=postgres
|
||||
DB_NAME=fusero
|
||||
JWT_SECRET=your_jwt_secret_key_here
|
||||
```
|
||||
|
||||
2. **Frontend Environment**
|
||||
- Copy `.env.example` to `.env` in the frontend directory
|
||||
- Set the API base URL:
|
||||
```
|
||||
VITE_API_BASE_URL=http://localhost:14000/api/v1
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
1. **Build and Run with Docker**
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
2. **Run Migrations and Seeders in Production**
|
||||
After your containers are up, run the following commands to apply database migrations and seed data inside the backend container:
|
||||
```bash
|
||||
docker exec -it fusero-app-backend npx mikro-orm migration:up
|
||||
docker exec -it fusero-app-backend npm run seed
|
||||
```
|
||||
**Note:** These commands must be run inside the backend container so they use the correct Docker network and environment variables.
|
||||
|
||||
3. **Environment Variables**
|
||||
- Ensure all environment variables are properly set in your production environment
|
||||
- Never commit `.env` files to version control
|
||||
|
||||
## Frontend Routing in Production
|
||||
|
||||
In production, the frontend is served through nginx. To ensure client-side routing works correctly:
|
||||
|
||||
1. **Nginx Configuration**
|
||||
- Ensure your nginx configuration includes the following directive to handle unknown routes:
|
||||
```nginx
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
```
|
||||
|
||||
2. **React Router Configuration**
|
||||
- Set the `basename` dynamically based on the environment:
|
||||
- In production, set `basename="/dashboard"`.
|
||||
- In development, set `basename="/"`.
|
||||
|
||||
3. **Navigation Links**
|
||||
- Use relative paths in your navigation links (e.g., `to="canvas/canvas-endpoints"` instead of `to="/dashboard/canvas/canvas-endpoints"`).
|
||||
|
||||
## HTTPS with Self-Signed Certificates
|
||||
|
||||
To run the application with HTTPS using a self-signed certificate:
|
||||
|
||||
1. **Generate a Self-Signed Certificate**
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./nginx/ssl/nginx.key -out ./nginx/ssl/nginx.crt
|
||||
```
|
||||
|
||||
2. **Update Docker Compose**
|
||||
- Ensure your `docker-compose.yml` mounts the certificate files in the nginx service:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./nginx/ssl:/etc/nginx/ssl
|
||||
```
|
||||
|
||||
3. **Nginx Configuration**
|
||||
- Use the production nginx configuration that includes SSL settings.
|
||||
|
||||
## Development Best Practices
|
||||
|
||||
1. **Database Management**
|
||||
- Always run the database in Docker
|
||||
- Use `docker-compose.dev.yml` for development
|
||||
- Never run PostgreSQL directly on your host machine
|
||||
|
||||
2. **Running Services Separately**
|
||||
- For development, it's recommended to run frontend and backend in separate terminal windows
|
||||
- This allows for better debugging and hot-reloading
|
||||
- You can see logs from each service clearly
|
||||
|
||||
3. **Code Organization**
|
||||
- Frontend code should be in the `frontend/` directory
|
||||
- Backend code should be in the `backend/` directory
|
||||
- Shared types and utilities should be in their respective directories
|
||||
|
||||
4. **Version Control**
|
||||
- Commit `package-lock.json` files
|
||||
- Don't commit `.env` files
|
||||
- Use meaningful commit messages
|
||||
|
||||
## API Documentation
|
||||
|
||||
The backend API is documented using Swagger/OpenAPI. After starting the backend service, you can access the API documentation at:
|
||||
- Development: http://localhost:14000/api-docs
|
||||
- Production: http://your-domain/api-docs
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. **Port Conflicts**
|
||||
- If you encounter port conflicts, check which services are running:
|
||||
```bash
|
||||
docker ps
|
||||
```
|
||||
- Or check for processes using the ports:
|
||||
```bash
|
||||
lsof -i :3000
|
||||
lsof -i :14000
|
||||
```
|
||||
|
||||
2. **Database Issues**
|
||||
- Ensure PostgreSQL is running in Docker
|
||||
- Check database connection settings in `.env`
|
||||
- Verify database migrations are up to date
|
||||
- If database issues persist, try:
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml down
|
||||
docker-compose -f docker-compose.dev.yml up db
|
||||
```
|
||||
|
||||
3. **CORS Issues**
|
||||
- If you see CORS errors, verify the frontend's API base URL
|
||||
- Check backend CORS configuration
|
||||
- Ensure both services are running on the correct ports
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Create a new branch for your feature
|
||||
2. Make your changes
|
||||
3. Submit a pull request
|
||||
4. Ensure all tests pass
|
||||
5. Update documentation as needed
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## Technical Documentation: ChatGPT-Powered Endpoint Creation
|
||||
|
||||
### Overview
|
||||
Developers can leverage the ChatGPT modal in the Canvas Endpoints UI to create new Canvas API endpoints using natural language prompts. When a user enters a prompt like "Create a course endpoint for Canvas", the system uses ChatGPT to:
|
||||
|
||||
1. Interpret the intent and generate a JSON object with the required fields for the endpoint (name, method, path, description, etc.).
|
||||
2. Automatically submit this JSON to the backend endpoint creation API (`/api/v1/canvas-api/endpoints`).
|
||||
3. Refresh the endpoint list in the UI and display a success message.
|
||||
|
||||
### How it Works
|
||||
- **Prompt Handling:**
|
||||
- The frontend sends the user's prompt to `/api/v1/canvas-api/chatgpt/completions`.
|
||||
- ChatGPT is instructed to return only a JSON object suitable for the endpoint creation form.
|
||||
- **Auto-Creation:**
|
||||
- If the response is a valid endpoint JSON (with `name`, `method`, and `path`), the frontend posts it to `/api/v1/canvas-api/endpoints`.
|
||||
- The endpoint list is refreshed and a toast notification is shown.
|
||||
- **Fallback:**
|
||||
- If the response is not a valid endpoint JSON, it is displayed as a normal chat message.
|
||||
|
||||
### Example Prompt
|
||||
```
|
||||
Create a course endpoint for Canvas. Use the Canvas API docs to determine the correct path and required fields.
|
||||
```
|
||||
|
||||
### Example ChatGPT Response
|
||||
```
|
||||
{
|
||||
"name": "Create Course",
|
||||
"method": "POST",
|
||||
"path": "/courses",
|
||||
"description": "Creates a new course in Canvas."
|
||||
}
|
||||
```
|
||||
|
||||
### Developer Notes
|
||||
- The ChatGPT modal logic is in `frontend/src/components/CanvasEndpoints.tsx`.
|
||||
- The backend endpoint creation API is `/api/v1/canvas-api/endpoints`.
|
||||
- The system expects ChatGPT to return a JSON object with at least `name`, `method`, and `path`.
|
||||
- The endpoint list is auto-refreshed after creation.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Environment Setup
|
||||
|
||||
backend/.env:
|
||||
POSTGRES_NAME=fusero-boilerplate-db
|
||||
POSTGRES_HOSTNAME=localhost
|
||||
POSTGRES_PORT=19095
|
||||
POSTGRES_USER=root
|
||||
POSTGRES_PASSWORD=root123
|
||||
JWT_SECRET=your_jwt_secret_key_here
|
||||
|
||||
# For Kubernetes, these are set in chart/values.yaml:
|
||||
# POSTGRES_NAME=fusero-boilerplate-db
|
||||
# POSTGRES_HOSTNAME=postgres-service
|
||||
# POSTGRES_PORT=19095
|
||||
# POSTGRES_USER=root
|
||||
# POSTGRES_PASSWORD=root123
|
||||
|
||||
frontend/.env:
|
||||
VITE_API_BASE_URL=http://localhost:14000/api/v1
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Production Deployment
|
||||
|
||||
1. Build and run with Docker:
|
||||
docker-compose up --build
|
||||
|
||||
2. Apply migrations and seed inside backend container:
|
||||
docker exec -it fusero-app-backend npx mikro-orm migration:up
|
||||
docker exec -it fusero-app-backend npm run seed
|
||||
|
||||
3. Ensure all required environment variables are configured.
|
||||
Never commit `.env` files.
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Frontend Routing in Production
|
||||
|
||||
In production, the frontend is served through NGINX.
|
||||
|
||||
NGINX configuration (important for React routing):
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
React Router Configuration:
|
||||
Use `basename="/"` in dev, and `basename="/dashboard"` in production.
|
||||
|
||||
Use relative paths in links:
|
||||
Correct: to="canvas/canvas-endpoints"
|
||||
Wrong: to="/dashboard/canvas/canvas-endpoints"
|
||||
|
||||
---
|
||||
|
||||
## 🔐 HTTPS with Self-Signed Certificates
|
||||
|
||||
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:
|
||||
volumes:
|
||||
- ./nginx/ssl:/etc/nginx/ssl
|
||||
|
||||
Configure NGINX to use the cert in production.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Development Best Practices
|
||||
|
||||
- Always run the DB via Docker
|
||||
- Use `docker-compose.dev.yml` for development
|
||||
- Never run PostgreSQL directly on host
|
||||
- Run frontend and backend separately for hot reload
|
||||
- Use `.env.example` as a template
|
||||
- Never commit `.env`
|
||||
- Commit `package-lock.json`
|
||||
- Use meaningful commit messages
|
||||
|
||||
---
|
||||
|
||||
## 📘 API Documentation
|
||||
|
||||
After running the backend:
|
||||
|
||||
Development → http://localhost:14000/api-docs
|
||||
Production → https://your-domain/api-docs
|
||||
|
||||
---
|
||||
|
||||
## 🧩 ChatGPT-Powered Endpoint Creation
|
||||
|
||||
Prompts like "Create a course endpoint for Canvas" auto-generate API endpoints.
|
||||
|
||||
How it works:
|
||||
1. The frontend sends your prompt to `/api/v1/canvas-api/chatgpt/completions`
|
||||
2. If ChatGPT returns a valid endpoint JSON, it's POSTed to `/api/v1/canvas-api/endpoints`
|
||||
3. The UI auto-refreshes the endpoint list and shows a toast
|
||||
|
||||
Example Prompt:
|
||||
Create a course endpoint for Canvas.
|
||||
|
||||
Expected JSON:
|
||||
{
|
||||
"name": "Create Course",
|
||||
"method": "POST",
|
||||
"path": "/courses",
|
||||
"description": "Creates a new course in Canvas."
|
||||
}
|
||||
|
||||
Developer Notes:
|
||||
- Frontend logic: frontend/src/components/CanvasEndpoints.tsx
|
||||
- Backend API: /api/v1/canvas-api/endpoints
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Troubleshooting
|
||||
|
||||
Port Conflicts:
|
||||
docker ps
|
||||
lsof -i :3000
|
||||
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
|
||||
|
||||
CORS Issues:
|
||||
Check API base URL in frontend `.env`
|
||||
Check backend CORS settings
|
||||
Verify ports match and services are running
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Create a branch
|
||||
2. Make your changes
|
||||
3. Pass all tests
|
||||
4. Open a pull request
|
||||
5. Update docs if needed
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See the LICENSE file for full details.
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes Troubleshooting & Redeployment Commands
|
||||
|
||||
If your backend is not picking up environment variables or is failing to connect to the database, follow these steps:
|
||||
|
||||
### 1. Rebuild the backend Docker image (after code/config changes)
|
||||
```bash
|
||||
docker build -t fusero-backend-dev:local .
|
||||
```
|
||||
|
||||
### 2. (If using a remote registry) Push the image
|
||||
```bash
|
||||
docker push <your-registry>/fusero-backend-dev:local
|
||||
```
|
||||
|
||||
### 3. Upgrade the Helm release with the latest values
|
||||
```bash
|
||||
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
|
||||
```
|
||||
|
||||
### 4. Restart the backend deployment to pick up new images and env vars
|
||||
```bash
|
||||
kubectl rollout restart deployment/fusero-backend -n fusero
|
||||
```
|
||||
|
||||
### 5. Check backend pod environment variables
|
||||
```bash
|
||||
kubectl get pods -n fusero
|
||||
# Replace <backend-pod-name> with the actual pod name from above
|
||||
kubectl exec -n fusero <backend-pod-name> -- printenv | grep POSTGRES
|
||||
```
|
||||
|
||||
### 6. Check backend pod logs for errors
|
||||
```bash
|
||||
kubectl logs <backend-pod-name> -n fusero --tail=50
|
||||
```
|
||||
|
||||
### 7. If you change DB env vars or code, repeat steps 1-6
|
||||
|
||||
---
|
||||
|
||||
**Note:**
|
||||
- Make sure your backend code does NOT load `.env` at runtime in Kubernetes. It should use the environment variables provided by the pod.
|
||||
- If you see connection errors to the DB, always check the pod's environment and logs as above.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Rebuild & Redeploy (Kubernetes)
|
||||
|
||||
If you change the VITE_API_BASE_URL or any frontend environment variable, rebuild and redeploy the frontend:
|
||||
|
||||
### 1. Rebuild the frontend Docker image
|
||||
```bash
|
||||
docker build -t fusero-frontend-dev:local ./frontend
|
||||
```
|
||||
|
||||
### 2. (If using a remote registry) Push the image
|
||||
```bash
|
||||
docker push <your-registry>/fusero-frontend-dev:local
|
||||
```
|
||||
|
||||
### 3. Upgrade the Helm release
|
||||
```bash
|
||||
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
|
||||
```
|
||||
|
||||
### 4. Restart the frontend deployment
|
||||
```bash
|
||||
kubectl rollout restart deployment/fusero-frontend -n fusero
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Port-Forwarding for Local Access
|
||||
|
||||
To access your services running in Kubernetes from your local machine, use these commands:
|
||||
|
||||
### Frontend (React app)
|
||||
```bash
|
||||
kubectl port-forward -n fusero 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
|
||||
```
|
||||
- Access at: http://localhost:14000
|
||||
|
||||
---
|
||||
|
||||
## NGINX Backend Service Name: Docker Compose vs Kubernetes
|
||||
|
||||
**If your frontend uses NGINX to proxy API requests, you must update the backend service name depending on your environment:**
|
||||
|
||||
- **Docker Compose/local:** The backend may be named `fusero-app-backend`.
|
||||
- **Kubernetes:** The backend service is named `fusero-backend-service`.
|
||||
|
||||
### How to update the NGINX config for Kubernetes
|
||||
|
||||
Edit `frontend/nginx.conf`:
|
||||
|
||||
**Change this:**
|
||||
```nginx
|
||||
proxy_pass http://fusero-app-backend:14000/;
|
||||
```
|
||||
**To this:**
|
||||
```nginx
|
||||
proxy_pass http://fusero-backend-service:14000/;
|
||||
```
|
||||
|
||||
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.yaml
|
||||
kubectl rollout restart deployment/fusero-frontend -n fusero
|
||||
```
|
||||
|
||||
**If you see an NGINX error like `host not found in upstream`, this is the cause!**
|
||||
|
||||
---
|
||||
|
||||
## Cleaning Up Duplicate or Crashing Deployments and Pods in Kubernetes
|
||||
|
||||
If you see multiple frontend or backend pods (or CrashLoopBackOff errors), clean up your namespace with these steps:
|
||||
|
||||
### 1. List deployments and pods
|
||||
```bash
|
||||
kubectl get deployments -n fusero
|
||||
kubectl get pods -n fusero
|
||||
```
|
||||
|
||||
### 2. Delete old or crashing deployments (example IDs from your cluster)
|
||||
```bash
|
||||
kubectl delete deployment fusero-frontend-65cb8db99d -n fusero
|
||||
kubectl delete deployment fusero-frontend-74fcbb778 -n fusero
|
||||
```
|
||||
|
||||
### 3. Delete old or crashing pods (example IDs from your cluster)
|
||||
```bash
|
||||
kubectl delete pod fusero-frontend-65cb8db99d-f2lhr -n fusero
|
||||
kubectl delete pod fusero-frontend-74fcbb778-v89gm -n fusero
|
||||
```
|
||||
|
||||
**Tip:** Only keep the latest, healthy pods and deployments. If in doubt, check with `kubectl get deployments -n fusero` and `kubectl get pods -n fusero` before deleting.
|
||||
|
||||
---
|
||||
|
||||
## Debugging Frontend Pod Crashes: NGINX SSL Certificate Errors
|
||||
|
||||
If your frontend pod crashes with an error like:
|
||||
|
||||
```
|
||||
nginx: [emerg] cannot load certificate "/etc/nginx/certs/fusero-selfsigned.crt": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory)
|
||||
```
|
||||
|
||||
This means NGINX is trying to load an SSL certificate that does not exist in the pod.
|
||||
|
||||
### How to fix for Kubernetes (Recommended)
|
||||
1. Edit `frontend/nginx.conf`:
|
||||
- Change:
|
||||
```nginx
|
||||
listen 14443 ssl;
|
||||
ssl_certificate /etc/nginx/certs/fusero-selfsigned.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/fusero-selfsigned.key;
|
||||
```
|
||||
- To:
|
||||
```nginx
|
||||
listen 8080;
|
||||
# (remove the ssl_certificate and ssl_certificate_key lines)
|
||||
```
|
||||
2. Rebuild the frontend Docker image:
|
||||
```bash
|
||||
docker build --no-cache -t fusero-frontend-dev:local ./frontend
|
||||
```
|
||||
3. (If using a remote registry) Push the image.
|
||||
4. Redeploy with Helm:
|
||||
```bash
|
||||
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
|
||||
```
|
||||
5. Check pod status:
|
||||
```bash
|
||||
kubectl get pods -n fusero
|
||||
```
|
||||
|
||||
**This will make NGINX listen on port 8080 without SSL, which is standard for in-cluster Kubernetes services.**
|
||||
|
||||
---
|
||||
|
||||
## Connecting to the Database from Your Host (DBeaver, etc.)
|
||||
|
||||
To connect to the Postgres database running in Kubernetes from your local machine (for example, using DBeaver or another SQL client):
|
||||
|
||||
1. **Port-forward the Postgres service:**
|
||||
```bash
|
||||
kubectl port-forward svc/postgres-service 5432:5432
|
||||
```
|
||||
- Keep this terminal open while you use your database client.
|
||||
- If port 5432 is in use on your machine, you can use another local port (e.g., `15432:5432`) and connect to port 15432 in your client.
|
||||
|
||||
2. **Database connection settings:**
|
||||
- **Host:** `localhost`
|
||||
- **Port:** `5432` (or your chosen local port)
|
||||
- **Database:** `fusero-boilerplate-db`
|
||||
- **Username:** `root`
|
||||
- **Password:** `root123`
|
||||
|
||||
3. **Open DBeaver (or your preferred client) and create a new Postgres connection using the above settings.**
|
||||
|
||||
4. **Test the connection.**
|
||||
|
||||
---
|
13
architecture.excalidraw
Normal file
13
architecture.excalidraw
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor",
|
||||
"elements": [],
|
||||
"appState": {
|
||||
"gridSize": 20,
|
||||
"gridStep": 5,
|
||||
"gridModeEnabled": false,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
},
|
||||
"files": {}
|
||||
}
|
6
chart/Chart.yaml
Normal file
6
chart/Chart.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: fusero
|
||||
description: Fusero application Helm chart
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "1.0.0"
|
25
chart/templates/backend-deployment.yaml
Normal file
25
chart/templates/backend-deployment.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: fusero-backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: fusero-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: fusero-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: {{ .Values.backend.image }}
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.backend.port }}
|
||||
env:
|
||||
{{- range $key, $val := .Values.backend.env }}
|
||||
- name: {{ $key }}
|
||||
value: "{{ $val }}"
|
||||
{{- end }}
|
25
chart/templates/backend-migration.job.yaml
Normal file
25
chart/templates/backend-migration.job.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: fusero-backend-db-init
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
template:
|
||||
metadata:
|
||||
name: fusero-backend-db-init
|
||||
spec:
|
||||
containers:
|
||||
- name: migrate-seed
|
||||
image: {{ .Values.backend.image }}
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
echo "Running migrations and seeds..." && \
|
||||
npx mikro-orm migration:up && \
|
||||
npm run seed
|
||||
env:
|
||||
{{- range $key, $val := .Values.backend.env }}
|
||||
- name: {{ $key }}
|
||||
value: "{{ $val }}"
|
||||
{{- end }}
|
||||
restartPolicy: Never
|
2
chart/templates/backend-secrets.yaml
Normal file
2
chart/templates/backend-secrets.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
# You can skip this if you manage secrets separately
|
||||
# Included for completeness (values-dev.yaml handles them)
|
11
chart/templates/backend-service.yaml
Normal file
11
chart/templates/backend-service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: fusero-backend-service
|
||||
spec:
|
||||
selector:
|
||||
app: fusero-backend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: {{ .Values.backend.port }}
|
||||
targetPort: {{ .Values.backend.port }}
|
25
chart/templates/frontend-deployment.yaml
Normal file
25
chart/templates/frontend-deployment.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: fusero-frontend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: fusero-frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: fusero-frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: {{ .Values.frontend.image }}
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.frontend.port }}
|
||||
env:
|
||||
{{- range $key, $val := .Values.frontend.env }}
|
||||
- name: {{ $key }}
|
||||
value: "{{ $val }}"
|
||||
{{- end }}
|
11
chart/templates/frontend-service.yaml
Normal file
11
chart/templates/frontend-service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: fusero-frontend-service
|
||||
spec:
|
||||
selector:
|
||||
app: fusero-frontend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: {{ .Values.frontend.port }}
|
33
chart/templates/postgres-deployment.yaml
Normal file
33
chart/templates/postgres-deployment.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgres
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: {{ .Values.postgres.image }}
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: {{ .Values.postgres.dbName }}
|
||||
- name: POSTGRES_USER
|
||||
value: {{ .Values.postgres.user }}
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: {{ .Values.postgres.password }}
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/postgresql/data
|
||||
name: postgres-data
|
||||
volumes:
|
||||
- name: postgres-data
|
||||
persistentVolumeClaim:
|
||||
claimName: postgres-pvc-fresh
|
10
chart/templates/postgres-pvc.yaml
Normal file
10
chart/templates/postgres-pvc.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-pvc-fresh
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.postgres.storage }}
|
10
chart/templates/postgres-service.yaml
Normal file
10
chart/templates/postgres-service.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres-service
|
||||
spec:
|
||||
selector:
|
||||
app: postgres
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
@ -1,5 +1,5 @@
|
||||
# Build stage
|
||||
FROM node:18-alpine as build
|
||||
FROM node:18-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -1,18 +1,17 @@
|
||||
FROM node:18-alpine
|
||||
FROM node:22
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
# Copy package files and install dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application
|
||||
# Copy .env and source code
|
||||
COPY .env .
|
||||
COPY . .
|
||||
|
||||
# Expose port 8080
|
||||
# Expose the dev server port
|
||||
EXPOSE 8080
|
||||
|
||||
# Start development server
|
||||
# Start the dev server — CORRECT host
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "8080"]
|
@ -1,12 +1,13 @@
|
||||
server {
|
||||
listen 14443 ssl;
|
||||
# listen 14443 ssl;
|
||||
listen 8080;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fusero-selfsigned.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/fusero-selfsigned.key;
|
||||
# ssl_certificate /etc/nginx/certs/fusero-selfsigned.crt;
|
||||
# ssl_certificate_key /etc/nginx/certs/fusero-selfsigned.key;
|
||||
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://fusero-app-backend:14000/;
|
||||
proxy_pass http://fusero-backend-service:14000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
@ -15,11 +16,8 @@ server {
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://host.docker.internal:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
@ -6,6 +6,13 @@ export default defineConfig({
|
||||
base: '/',
|
||||
server: {
|
||||
port: 8080,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:14000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Force reload `.env` even if it was previously loaded
|
||||
dotenv.config({ override: true });
|
||||
import { Options } from '@mikro-orm/core';
|
||||
import { PostgreSqlDriver } from '@mikro-orm/postgresql';
|
||||
import { Migrator } from '@mikro-orm/migrations';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
if (process.env.KUBERNETES_SERVICE_HOST === undefined) {
|
||||
dotenv.config({ override: true });
|
||||
}
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
|
11
package.json
11
package.json
@ -19,6 +19,14 @@
|
||||
"start": "npm run build:ts && fastify start -l info -r tsconfig-paths/register dist/src/app.js",
|
||||
"prebuild": "npm run lint",
|
||||
"build:ts": "rimraf dist && tsc",
|
||||
"build:frontend:dev": "docker build -t fusero-frontend-dev:local -f frontend/Dockerfile.dev ./frontend",
|
||||
"k8s:dev:apply": "kubectl apply -f k8s/frontend/deployment.dev.yml",
|
||||
"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",
|
||||
"k8s:dev:svc": "kubectl describe svc fusero-frontend-service",
|
||||
"k8s:dev:deployment": "kubectl describe deployment fusero-frontend-dev",
|
||||
"k8s:get": "kubectl get pods,svc,deployment",
|
||||
"watch:ts": "tsc -w",
|
||||
"dev": "npm run build:ts && concurrently -k -p \"[{name}]\" -n \"TypeScript,App\" -c \"yellow.bold,cyan.bold\" \"npm:watch:ts\" \"npm:dev:start\"",
|
||||
"dev:start": "fastify start --ignore-watch=.ts$ -w -l info -P -r tsconfig-paths/register dist/src/app.js",
|
||||
@ -27,7 +35,8 @@
|
||||
"lint:fix": "eslint src/**/*.{js,jsx,ts,tsx} --fix",
|
||||
"migration:create": "npx mikro-orm migration:create",
|
||||
"seed": "ts-node -r tsconfig-paths/register src/database/seeds/run-seed.ts",
|
||||
"test:db": "ts-node -r tsconfig-paths/register src/database/test-connection.ts"
|
||||
"test:db": "ts-node -r tsconfig-paths/register src/database/test-connection.ts",
|
||||
"k8s:exec": "kubectl exec -it $POD_NAME -- /bin/sh"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
@ -80,7 +80,14 @@ const app: FastifyPluginAsync = async (app, opts): Promise<void> => {
|
||||
|
||||
// Register CORS, JWT, and Cookies
|
||||
app.register(fastifyCors, {
|
||||
origin: ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:8080', 'http://localhost:8081'],
|
||||
origin: process.env.CORS_ORIGIN
|
||||
? process.env.CORS_ORIGIN.split(',').map(origin => origin.trim())
|
||||
: [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:3001',
|
||||
'http://localhost:8080',
|
||||
'http://localhost:8081'
|
||||
],
|
||||
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization', 'Accept'],
|
||||
credentials: true,
|
||||
|
Loading…
Reference in New Issue
Block a user