Go to file
2025-05-21 10:13:38 +02:00
chart revised complete k8s deployment 2025-05-21 10:13:38 +02:00
docs update readme 2025-05-20 13:00:00 +02:00
frontend mega update: k8s backend, frontend, database, pods, helm charts, k8s, load balancing, gitea-ci.yml explanded readme 2025-05-20 12:39:47 +02:00
nginx udpated api proxy 2025-05-15 18:55:18 +02:00
src Save current state before cleaning history 2025-05-21 09:27:45 +02:00
test first commit - backend init 2025-04-29 07:51:17 +02:00
.env.example updates across the board 2025-05-15 15:10:07 +02:00
.eslintrc.json first commit - backend init 2025-04-29 07:51:17 +02:00
.gitea-ci.yml revised complete k8s deployment 2025-05-21 10:13:38 +02:00
.gitignore revised complete k8s deployment 2025-05-21 10:13:38 +02:00
.prettierrc.json mega update: k8s backend, frontend, database, pods, helm charts, k8s, load balancing, gitea-ci.yml explanded readme 2025-05-20 12:39:47 +02:00
architecture.excalidraw mega update: k8s backend, frontend, database, pods, helm charts, k8s, load balancing, gitea-ci.yml explanded readme 2025-05-20 12:39:47 +02:00
docker-compose.dev.yml first push frontend 2025-04-30 17:34:49 +02:00
docker-compose.yml fixed frontend issues 2025-05-15 18:11:05 +02:00
Dockerfile first commit - backend init 2025-04-29 07:51:17 +02:00
jest.config.js first commit - backend init 2025-04-29 07:51:17 +02:00
mikro-orm.config.ts mega update: k8s backend, frontend, database, pods, helm charts, k8s, load balancing, gitea-ci.yml explanded readme 2025-05-20 12:39:47 +02:00
package-lock.json first push frontend 2025-04-30 17:34:49 +02:00
package.json revised complete k8s deployment 2025-05-21 10:13:38 +02:00
README.md revised complete k8s deployment 2025-05-21 10:13:38 +02:00
tsconfig.json first commit - backend init 2025-04-29 07:51:17 +02:00

Fusero App Boilerplate

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.


📚 Table of Contents


📁 Project Structure

fusero-app-boilerplate/ ├── chart/ # Helm chart for Kubernetes │ ├── Chart.yaml │ ├── values.dev.yaml │ ├── values.prod.yaml │ └── templates/ ├── config/ ├── coverage/ ├── dist/ ├── docs/ ├── frontend/ # React frontend app │ ├── public/ │ └── src/ ├── mikro-orm.config.ts ├── nginx/ ├── node_modules/ ├── package.json ├── package-lock.json ├── docker-compose.yaml ├── docker-compose.dev.yaml ├── .gitignore ├── .gitea-ci.yaml ├── .prettierrc.json ├── .eslintrc.json ├── architecture.excalidraw ├── src/ # Node.js backend source │ ├── apps/ │ ├── constants/ │ ├── database/ │ ├── middleware/ │ ├── plugins/ │ ├── shared/ │ ├── tests/ │ ├── types/ │ └── ... ├── test/ ├── utils/ └── README.md


⚙️ Prerequisites

  • Node.js (v20 or higher)
  • npm (v9 or higher)
  • Docker & Docker Compose
  • Git

💻 Development Setup

🗃️ PostgreSQL must run in Docker for consistent behavior.

Create volume and start the database:
docker volume create fusero-dev-db-data
docker-compose up -d db

Backend setup:
cd backend
cp .env.example .env
npm install

To create a new migration:

npm run migration:create

To apply migrations:

npm run migrate

To seed the database:

npm run seed
npm run dev &
cd ..

Frontend setup:
cd frontend
cp .env.example .env
npm install
npm run dev &
cd ..

App is running:
Frontend → http://localhost:3000
Backend → http://localhost:14000


Alternate: Running Services in Separate Terminals

Terminal 1 (backend):
cd backend
npm install
npm run dev

Terminal 2 (frontend):
cd frontend
npm install
npm run dev


🛠️ 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


🐳 Docker Development

🗃️ PostgreSQL must run in Docker for consistent behavior.

Create volume and start the database:
docker volume create fusero-dev-db-data
docker-compose up -d db

Backend setup:
cd backend
cp .env.example .env
npm install

To create a new migration:

npm run migration:create

To apply migrations:

npm run migrate

To seed the database:

npm run seed
npm run dev &
cd ..

Frontend setup:
cd frontend
cp .env.example .env
npm install
npm run dev &
cd ..

App is running:
Frontend → http://localhost:3000
Backend → http://localhost:14000


🚀 Kubernetes 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 npm run migrate
    docker exec -it fusero-app-backend npm run seed

  3. Ensure all required environment variables are configured.


🌐 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.yaml 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.yaml 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.yaml down
docker-compose -f docker-compose.dev.yaml 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)

docker build -t fusero-backend-dev:local .

2. (If using a remote registry) Push the image

docker push <your-registry>/fusero-backend-dev:local

3. Upgrade the Helm release with the latest values

helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml

4. Restart the backend deployment to pick up new images and env vars

kubectl rollout restart deployment/fusero-backend -n fusero

5. Check backend pod environment variables

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

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

docker build -t fusero-frontend-dev:local ./frontend

2. (If using a remote registry) Push the image

docker push <your-registry>/fusero-frontend-dev:local

3. Upgrade the Helm release

helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml

4. Restart the frontend deployment

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)

kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80

Backend (API)

kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000

Database

kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432
  • Access at: localhost:5432

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:

proxy_pass http://fusero-app-backend:14000/;

To this:

proxy_pass http://fusero-backend-service:14000/;

Then rebuild and redeploy the frontend:

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

kubectl get deployments -n fusero
kubectl get pods -n fusero

2. Delete old or crashing deployments (example IDs from your cluster)

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)

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.

  1. Edit frontend/nginx.conf:
    • Change:
      listen 14443 ssl;
      ssl_certificate /etc/nginx/certs/fusero-selfsigned.crt;
      ssl_certificate_key /etc/nginx/certs/fusero-selfsigned.key;
      
    • To:
      listen 8080;
      # (remove the ssl_certificate and ssl_certificate_key lines)
      
  2. Rebuild the frontend Docker image:
    docker build --no-cache -t fusero-frontend-dev:local ./frontend
    
  3. (If using a remote registry) Push the image.
  4. Redeploy with Helm:
    helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
    
  5. Check pod status:
    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:

    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.


🎯 Kubernetes Namespace Management

Development Namespace Setup

# Create development namespace
kubectl create namespace fusero-dev

# Set current context to development namespace
kubectl config set-context --current --namespace=fusero-dev

# Deploy to development namespace
helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml

Production Namespace Setup

# Create production namespace
kubectl create namespace fusero-prod

# Set current context to production namespace
kubectl config set-context --current --namespace=fusero-prod

# Deploy to production namespace
helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml

Namespace Management Commands

# List all namespaces
kubectl get namespaces

# Switch between namespaces
kubectl config set-context --current --namespace=<namespace-name>

# View resources in current namespace
kubectl get all

# Delete namespace (be careful!)
kubectl delete namespace <namespace-name>
  1. Lens - The most popular Kubernetes IDE

    • Download: https://k8slens.dev/
    • Features:
      • Real-time cluster monitoring
      • Multi-cluster management
      • Namespace isolation
      • Resource visualization
      • Log streaming
      • Terminal access
  2. K9s - Terminal-based UI

    • Install: brew install k9s (Mac) or scoop install k9s (Windows)
    • Features:
      • Fast navigation
      • Resource management
      • Log viewing
      • Port forwarding
  3. Octant - Web-based UI

    • Install: https://octant.dev/
    • Features:
      • Resource visualization
      • Configuration management
      • Log viewing
      • Port forwarding

🆕 Namespaced Development Environment with Helm

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.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:

  1. Create the development namespace (if not already created):

    kubectl create namespace fusero-dev
    
  2. Deploy all services to the namespace:

    helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml
    
  3. Check running pods and services:

    kubectl get all -n fusero-dev
    
  4. If you update environment variables or service hostnames, repeat the Helm upgrade command.

  5. If you see errors about immutable fields (e.g., for Jobs), delete the old Job before redeploying:

    kubectl delete job <job-name> -n fusero-dev
    helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml
    

Why use namespaces?

  • Keeps dev and prod resources isolated
  • Makes it easy to clean up or redeploy all dev resources
  • Prevents accidental cross-environment access

🔒 Production Security & Best Practices

Environment Variables & Secrets

The application uses a secure secrets management approach:

  1. Development Environment:

    • Use .env files locally (gitignored)
    • Copy from .env.example as template
  2. Production Environment:

    • Secrets are managed through Gitea CI/CD secrets
    • Template file: chart/secrets.prod.template.yaml
    • Actual secrets are generated during deployment
    • Never commit actual secrets to the repository
  3. Required Secrets:

    • Database credentials
    • Admin user credentials
    • Security keys (encryption, JWT)
    • API keys (ChatGPT, Canvas)
  4. Secrets in CI/CD:

    • Secrets are stored in Gitea CI/CD settings
    • Automatically injected during deployment
    • Used to generate secrets.prod.yaml at runtime
  5. Security Best Practices:

    • All secrets files are gitignored
    • Template files contain placeholder values
    • Production secrets are never stored in the repository
    • Regular rotation of secrets recommended

HTTPS & Certificates

  • In production, use trusted certificates (e.g., Let's Encrypt).
  • Configure NGINX to enforce HTTPS.

CORS & Security Headers

  • Lock down CORS settings in production.
  • Use security headers (e.g., HSTS, CSP).

Logging & Monitoring

  • Configure logging for production (e.g., ELK stack, Datadog).
  • Set up basic monitoring (e.g., Prometheus, Grafana).

Database Backup

  • Regularly backup your production database.
  • Example: Use pg_dump or a managed backup service.

CI/CD & Automated Testing

  • Run tests before deploying to production.
  • Example CI/CD workflow:
    # .github/workflows/deploy.yml
    name: Deploy to Production
    on:
      push:
        branch: main
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - name: Run Tests
            run: npm test
          - name: Deploy to Kubernetes
            run: helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml
    

Troubleshooting Production

  • Common issues:
    • Database connection errors: Check secrets and network policies.
    • Pod crashes: Check logs with kubectl logs <pod-name> -n fusero-prod.
  • Rollback: Use helm rollback fusero <revision> -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:
      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:
      kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000
      
    • Frontend:
      kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80
      
    • Database:
      kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432
      
  • Testing Login with curl:
    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:
        kubectl logs -n fusero-dev -l app=fusero-backend --tail=100