Compare commits
36 Commits
hotfix-pat
...
main
Author | SHA1 | Date | |
---|---|---|---|
4a7532fb85 | |||
9786b8da06 | |||
633a4e95c2 | |||
2bf8d04657 | |||
5820150ec9 | |||
eb079fa9e4 | |||
079018a77e | |||
6f6ddb072b | |||
7e132608d7 | |||
03f615bf57 | |||
b2706bc747 | |||
5c3833a9e4 | |||
e5d7a52e59 | |||
55feccec4b | |||
fda611d249 | |||
fe6ceb5b39 | |||
2929e7b83f | |||
717b9978d5 | |||
5e12a9cb25 | |||
fa08e6de1b | |||
addcc6d7e2 | |||
c8ddb9e732 | |||
45a2ba4611 | |||
576ce89698 | |||
f3bece7009 | |||
2b589a93b2 | |||
458bfb9af1 | |||
0c66dd65c5 | |||
fdb61a21bf | |||
5ef9cfdaa9 | |||
893e36d8c9 | |||
fef95ff9eb | |||
4499e11603 | |||
c69cd4768c | |||
e801a3b927 | |||
e46cfeaa4b |
@ -31,10 +31,16 @@ jobs:
|
|||||||
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-backend:buildcache
|
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-backend:buildcache
|
||||||
cache-to: type=registry,ref=registry.liquidrinu.com/fusero-backend:buildcache,mode=max
|
cache-to: type=registry,ref=registry.liquidrinu.com/fusero-backend:buildcache,mode=max
|
||||||
|
|
||||||
|
- name: Create .env file
|
||||||
|
run: |
|
||||||
|
echo "VITE_API_BASE_URL=/api" > ./frontend/.env
|
||||||
|
# This only affects the CI/CD build, not your local dev .env
|
||||||
|
|
||||||
- name: Build and Push Frontend
|
- name: Build and Push Frontend
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
|
file: ./frontend/Dockerfile.dev
|
||||||
push: true
|
push: true
|
||||||
tags: registry.liquidrinu.com/fusero-frontend:latest
|
tags: registry.liquidrinu.com/fusero-frontend:latest
|
||||||
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-frontend:buildcache
|
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-frontend:buildcache
|
||||||
@ -68,6 +74,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
kubectl delete job fusero-backend-db-init -n fusero-prod || true
|
kubectl delete job fusero-backend-db-init -n fusero-prod || true
|
||||||
|
|
||||||
|
- name: Install Helm
|
||||||
|
run: |
|
||||||
|
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||||
|
|
||||||
|
- name: Generate values.prod.yaml from template
|
||||||
|
run: cp chart/values.prod.template.yaml chart/values.prod.yaml
|
||||||
|
|
||||||
- name: Deploy to Kubernetes
|
- name: Deploy to Kubernetes
|
||||||
run: |
|
run: |
|
||||||
helm upgrade --install fusero ./chart \
|
helm upgrade --install fusero ./chart \
|
||||||
@ -75,8 +88,8 @@ jobs:
|
|||||||
--create-namespace \
|
--create-namespace \
|
||||||
--values ./chart/values.prod.yaml \
|
--values ./chart/values.prod.yaml \
|
||||||
--values ./chart/secrets.prod.yaml \
|
--values ./chart/secrets.prod.yaml \
|
||||||
--set backend.image.repository=registry.liquidrinu.com/fusero-backend \
|
--set backend.image=registry.liquidrinu.com/fusero-backend:latest \
|
||||||
--set frontend.image.repository=registry.liquidrinu.com/fusero-frontend
|
--set frontend.image=registry.liquidrinu.com/fusero-frontend:latest
|
||||||
|
|
||||||
- name: Wait for migration/seed job
|
- name: Wait for migration/seed job
|
||||||
run: |
|
run: |
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -143,3 +143,7 @@ chart/secrets.*.yaml
|
|||||||
!chart/values.prod.public.yaml
|
!chart/values.prod.public.yaml
|
||||||
|
|
||||||
.bkup/
|
.bkup/
|
||||||
|
|
||||||
|
# Ignore production values and secrets files
|
||||||
|
chart/values.prod.yaml
|
||||||
|
chart/secrets.prod.yaml
|
||||||
|
138
README.md
138
README.md
@ -10,44 +10,9 @@ A full-stack application boilerplate with a React frontend and Node.js backend
|
|||||||
- [📚 Table of Contents](#-table-of-contents)
|
- [📚 Table of Contents](#-table-of-contents)
|
||||||
- [📁 Project Structure](#-project-structure)
|
- [📁 Project Structure](#-project-structure)
|
||||||
- [⚙️ Prerequisites](#️-prerequisites)
|
- [⚙️ Prerequisites](#️-prerequisites)
|
||||||
- [💻 Development Setup](#-development-setup)
|
- [Development Setup](#development-setup)
|
||||||
- [To create a new migration:](#to-create-a-new-migration)
|
- [Important Note: Database Must Run in Docker](#important-note-database-must-run-in-docker)
|
||||||
- [npm run migration:create](#npm-run-migrationcreate)
|
|
||||||
- [To apply migrations:](#to-apply-migrations)
|
|
||||||
- [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.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)
|
|
||||||
- [🐳 Docker Development](#-docker-development)
|
|
||||||
- [To create a new migration:](#to-create-a-new-migration-1)
|
|
||||||
- [npm run migration:create](#npm-run-migrationcreate-1)
|
|
||||||
- [To apply migrations:](#to-apply-migrations-1)
|
|
||||||
- [To seed the database:](#to-seed-the-database-1)
|
|
||||||
- [🚀 Kubernetes Deployment](#-kubernetes-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)
|
- [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)
|
- [3. Upgrade the Helm release](#3-upgrade-the-helm-release)
|
||||||
- [4. Restart the frontend deployment](#4-restart-the-frontend-deployment)
|
- [4. Restart the frontend deployment](#4-restart-the-frontend-deployment)
|
||||||
- [Port-Forwarding for Local Access](#port-forwarding-for-local-access)
|
- [Port-Forwarding for Local Access](#port-forwarding-for-local-access)
|
||||||
@ -82,12 +47,17 @@ A full-stack application boilerplate with a React frontend and Node.js backend
|
|||||||
- [Troubleshooting Production](#troubleshooting-production)
|
- [Troubleshooting Production](#troubleshooting-production)
|
||||||
- [🆕 Recent Improvements \& Troubleshooting](#-recent-improvements--troubleshooting)
|
- [🆕 Recent Improvements \& Troubleshooting](#-recent-improvements--troubleshooting)
|
||||||
- [🚀 Production Deployment Pipeline (CI/CD)](#-production-deployment-pipeline-cicd)
|
- [🚀 Production Deployment Pipeline (CI/CD)](#-production-deployment-pipeline-cicd)
|
||||||
|
- [CI/CD Kubernetes Deployment Setup](#cicd-kubernetes-deployment-setup)
|
||||||
|
- [Using Private Docker Registry with Kubernetes](#using-private-docker-registry-with-kubernetes)
|
||||||
|
- [Production Secrets Management (Gitea as Source of Truth)](#production-secrets-management-gitea-as-source-of-truth)
|
||||||
|
- [CI/CD Pipeline Behavior: Multiple Merges to Main](#cicd-pipeline-behavior-multiple-merges-to-main)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📁 Project Structure
|
## 📁 Project Structure
|
||||||
|
|
||||||
fusero-app-boilerplate/
|
```
|
||||||
|
fusero-app-boilerplate
|
||||||
├── chart/ # Helm chart for Kubernetes
|
├── chart/ # Helm chart for Kubernetes
|
||||||
│ ├── Chart.yaml
|
│ ├── Chart.yaml
|
||||||
│ ├── values.dev.yaml
|
│ ├── values.dev.yaml
|
||||||
@ -125,6 +95,7 @@ fusero-app-boilerplate/
|
|||||||
├── test/
|
├── test/
|
||||||
├── utils/
|
├── utils/
|
||||||
└── README.md
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -557,3 +528,94 @@ The application uses a secure secrets management approach:
|
|||||||
- This ensures your database is always migrated and seeded with every deploy, and you'll know immediately if something goes wrong.
|
- This ensures your database is always migrated and seeded with every deploy, and you'll know immediately if something goes wrong.
|
||||||
|
|
||||||
- To trigger a production deployment, just push or merge to `main`.
|
- To trigger a production deployment, just push or merge to `main`.
|
||||||
|
|
||||||
|
## CI/CD Kubernetes Deployment Setup
|
||||||
|
|
||||||
|
To enable automated deployment to your Kubernetes cluster from CI/CD (Gitea Actions):
|
||||||
|
|
||||||
|
1. **Get your kubeconfig file from your Kubernetes master node or provider.**
|
||||||
|
- For self-hosted clusters, it's usually at `~/.kube/config` on the master node.
|
||||||
|
- For managed clusters, download it from your provider's dashboard.
|
||||||
|
|
||||||
|
2. **Edit the kubeconfig file:**
|
||||||
|
- Change the `server:` field to use your cluster's public IP or DNS, e.g.:
|
||||||
|
```yaml
|
||||||
|
server: https://[YOUR_PUBLIC_IP_OR_DNS]:6443
|
||||||
|
```
|
||||||
|
(For IPv6, use square brackets around the address.)
|
||||||
|
|
||||||
|
3. **Base64-encode the kubeconfig file as a single line:**
|
||||||
|
- On Linux:
|
||||||
|
```bash
|
||||||
|
base64 -w 0 /path/to/your/kubeconfig
|
||||||
|
```
|
||||||
|
- On Mac:
|
||||||
|
```bash
|
||||||
|
base64 /path/to/your/kubeconfig | tr -d '\n'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Add the base64 string as a secret in your Gitea repository:**
|
||||||
|
- Go to **Settings → Secrets**
|
||||||
|
- Name: `KUBE_CONFIG`
|
||||||
|
- Value: (paste the base64 string)
|
||||||
|
|
||||||
|
5. **Make sure port 6443 is open to your CI/CD runner's IP in your VPS firewall/security group.**
|
||||||
|
|
||||||
|
6. **Your pipeline will now be able to deploy to your Kubernetes cluster.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using Private Docker Registry with Kubernetes
|
||||||
|
|
||||||
|
If you use a private Docker registry (like registry.liquidrinu.com), you must create a Kubernetes secret and reference it in your deployments:
|
||||||
|
|
||||||
|
1. **Create the registry secret:**
|
||||||
|
```bash
|
||||||
|
kubectl create secret docker-registry regcred \
|
||||||
|
--docker-server=registry.liquidrinu.com \
|
||||||
|
--docker-username=YOUR_REGISTRY_USERNAME \
|
||||||
|
--docker-password=YOUR_REGISTRY_PASSWORD \
|
||||||
|
--docker-email=YOUR_EMAIL \
|
||||||
|
-n fusero-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Reference the secret in your deployment YAMLs:**
|
||||||
|
In your deployment spec, add:
|
||||||
|
```yaml
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
|
```
|
||||||
|
Example:
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows Kubernetes to authenticate to your private registry and pull images securely.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Production Secrets Management (Gitea as Source of Truth)
|
||||||
|
|
||||||
|
- In production, all sensitive values (like `POSTGRES_PASSWORD`, `DEFAULT_ADMIN_PASSWORD`, etc.) are managed as secrets in your Gitea repository (Settings → Secrets).
|
||||||
|
- The CI/CD pipeline uses these secrets to generate `chart/secrets.prod.yaml` and other files at runtime.
|
||||||
|
- Helm uses these generated files to set environment variables for your Kubernetes resources.
|
||||||
|
- The Postgres password is set from the secret **only when the database is first initialized** (i.e., when the persistent volume is empty). Changing the secret later will not update the password for an existing database unless you reset the DB or delete the volume.
|
||||||
|
- **Summary:** Gitea secrets are the source of truth for production. Always update secrets in Gitea and redeploy to apply changes to new pods.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CI/CD Pipeline Behavior: Multiple Merges to Main
|
||||||
|
|
||||||
|
- If multiple merges or pushes happen to the `main` branch in quick succession, your CI/CD system will start a separate pipeline for each commit.
|
||||||
|
- These pipelines will run in parallel unless your CI/CD is configured to queue or cancel previous runs.
|
||||||
|
- This can result in race conditions, where the last pipeline to finish will determine the final deployed state.
|
||||||
|
- **Best practice:** Avoid merging multiple large changes to `main` at the same time. Wait for the pipeline to finish before merging the next PR, or configure your CI/CD to cancel previous runs on new pushes.
|
||||||
|
|
||||||
|
---
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
apiVersion: v2
|
|
||||||
name: fusero
|
|
||||||
description: Fusero App Boilerplate Helm Chart
|
|
||||||
type: application
|
|
||||||
version: 0.1.0
|
|
||||||
appVersion: "1.0.0"
|
|
@ -12,6 +12,8 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: fusero-backend
|
app: fusero-backend
|
||||||
spec:
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
containers:
|
containers:
|
||||||
- name: backend
|
- name: backend
|
||||||
image: {{ .Values.backend.image }}
|
image: {{ .Values.backend.image }}
|
||||||
|
@ -8,18 +8,62 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
name: fusero-backend-db-init
|
name: fusero-backend-db-init
|
||||||
spec:
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
containers:
|
containers:
|
||||||
- name: migrate-seed
|
- name: migrate-seed
|
||||||
image: {{ .Values.backend.image }}
|
image: {{ .Values.backend.image }}
|
||||||
command: ["/bin/sh", "-c"]
|
command: ["/bin/sh", "-c"]
|
||||||
args:
|
args:
|
||||||
- |
|
- |
|
||||||
echo "Running migrations and seeds..." && \
|
echo "=== Environment Variables ==="
|
||||||
npx mikro-orm migration:up && \
|
env | grep -i postgres
|
||||||
|
echo "=== Testing Connection ==="
|
||||||
|
PGPASSWORD=$POSTGRES_PASSWORD psql "postgresql://$POSTGRES_USER@$POSTGRES_HOSTNAME:$POSTGRES_PORT/$POSTGRES_NAME" -c "SELECT 1"
|
||||||
|
echo "=== Running Migrations ==="
|
||||||
|
npx mikro-orm migration:up
|
||||||
|
echo "=== Running Seeds ==="
|
||||||
npm run seed
|
npm run seed
|
||||||
env:
|
env:
|
||||||
{{- range $key, $val := .Values.backend.env }}
|
- name: POSTGRES_HOSTNAME
|
||||||
- name: {{ $key }}
|
value: "postgres-service"
|
||||||
value: "{{ $val }}"
|
- name: POSTGRES_PORT
|
||||||
{{- end }}
|
value: "5432"
|
||||||
|
- name: POSTGRES_NAME
|
||||||
|
value: "fusero-db"
|
||||||
|
- name: POSTGRES_USER
|
||||||
|
value: "prod_admin"
|
||||||
|
- name: POSTGRES_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: fusero-backend-secrets
|
||||||
|
key: POSTGRES_PASSWORD
|
||||||
|
- name: NODE_ENV
|
||||||
|
value: "production"
|
||||||
|
- name: DEFAULT_ADMIN_USERNAME
|
||||||
|
value: "{{ .Values.backend.env.DEFAULT_ADMIN_USERNAME }}"
|
||||||
|
- name: DEFAULT_ADMIN_EMAIL
|
||||||
|
value: "{{ .Values.backend.env.DEFAULT_ADMIN_EMAIL }}"
|
||||||
|
- name: DEFAULT_ADMIN_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: fusero-backend-secrets
|
||||||
|
key: DEFAULT_ADMIN_PASSWORD
|
||||||
|
- name: JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: fusero-backend-secrets
|
||||||
|
key: JWT_SECRET
|
||||||
|
- name: CHATGPT_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: fusero-backend-secrets
|
||||||
|
key: CHATGPT_API_KEY
|
||||||
|
- name: CANVAS_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: fusero-backend-secrets
|
||||||
|
key: CANVAS_API_KEY
|
||||||
|
- name: CANVAS_API_URL
|
||||||
|
value: "{{ .Values.backend.env.CANVAS_API_URL }}"
|
||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
|
@ -12,6 +12,8 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: fusero-frontend
|
app: fusero-frontend
|
||||||
spec:
|
spec:
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
containers:
|
containers:
|
||||||
- name: frontend
|
- name: frontend
|
||||||
image: {{ .Values.frontend.image }}
|
image: {{ .Values.frontend.image }}
|
||||||
|
@ -8,11 +8,51 @@ data:
|
|||||||
local all all trust
|
local all all trust
|
||||||
host all all 127.0.0.1/32 trust
|
host all all 127.0.0.1/32 trust
|
||||||
host all all ::1/128 trust
|
host all all ::1/128 trust
|
||||||
host all all 0.0.0.0/0 md5
|
host all all 0.0.0.0/0 scram-sha-256
|
||||||
postgresql.conf: |
|
postgresql.conf: |
|
||||||
|
# Connection Settings
|
||||||
listen_addresses = '*'
|
listen_addresses = '*'
|
||||||
max_connections = 100
|
max_connections = 100
|
||||||
|
|
||||||
|
# Memory Settings
|
||||||
shared_buffers = 128MB
|
shared_buffers = 128MB
|
||||||
dynamic_shared_memory_type = posix
|
work_mem = 4MB
|
||||||
|
maintenance_work_mem = 64MB
|
||||||
|
|
||||||
|
# Write Ahead Log
|
||||||
max_wal_size = 1GB
|
max_wal_size = 1GB
|
||||||
min_wal_size = 80MB
|
min_wal_size = 80MB
|
||||||
|
checkpoint_timeout = 5min
|
||||||
|
checkpoint_completion_target = 0.9
|
||||||
|
|
||||||
|
# Query Planner
|
||||||
|
random_page_cost = 1.1
|
||||||
|
effective_cache_size = 4GB
|
||||||
|
|
||||||
|
# Autovacuum
|
||||||
|
autovacuum = on
|
||||||
|
autovacuum_max_workers = 3
|
||||||
|
autovacuum_naptime = 1min
|
||||||
|
autovacuum_vacuum_threshold = 50
|
||||||
|
autovacuum_analyze_threshold = 50
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log_min_duration_statement = 1000
|
||||||
|
log_checkpoints = on
|
||||||
|
log_connections = on
|
||||||
|
log_disconnections = on
|
||||||
|
log_lock_waits = on
|
||||||
|
log_temp_files = 0
|
||||||
|
log_autovacuum_min_duration = 0
|
||||||
|
|
||||||
|
# Other Settings
|
||||||
|
dynamic_shared_memory_type = posix
|
||||||
|
effective_io_concurrency = 200
|
||||||
|
default_statistics_target = 100
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
password_encryption = scram-sha-256
|
||||||
|
|
||||||
|
# Error Reporting
|
||||||
|
log_min_error_statement = error
|
||||||
|
log_statement = 'all'
|
@ -19,11 +19,42 @@ spec:
|
|||||||
- containerPort: 5432
|
- containerPort: 5432
|
||||||
env:
|
env:
|
||||||
- name: POSTGRES_DB
|
- name: POSTGRES_DB
|
||||||
value: {{ .Values.postgres.dbName }}
|
value: "{{ .Values.postgres.dbName }}"
|
||||||
- name: POSTGRES_USER
|
- name: POSTGRES_USER
|
||||||
value: {{ .Values.postgres.user }}
|
value: "{{ .Values.postgres.user }}"
|
||||||
- name: POSTGRES_PASSWORD
|
- name: POSTGRES_PASSWORD
|
||||||
value: {{ .Values.postgres.password }}
|
value: "{{ .Values.postgres.password }}"
|
||||||
|
- name: POSTGRES_HOST_AUTH_METHOD
|
||||||
|
value: "scram-sha-256"
|
||||||
|
startupProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- pg_isready
|
||||||
|
- -U
|
||||||
|
- "{{ .Values.postgres.user }}"
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
failureThreshold: 30
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- pg_isready
|
||||||
|
- -U
|
||||||
|
- "{{ .Values.postgres.user }}"
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 5
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- pg_isready
|
||||||
|
- -U
|
||||||
|
- "{{ .Values.postgres.user }}"
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /var/lib/postgresql/data
|
- mountPath: /var/lib/postgresql/data
|
||||||
name: postgres-data
|
name: postgres-data
|
||||||
|
69
chart/values.prod.template.yaml
Normal file
69
chart/values.prod.template.yaml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
global:
|
||||||
|
namespace: fusero-prod
|
||||||
|
security:
|
||||||
|
cors:
|
||||||
|
origin: "https://your-domain.com"
|
||||||
|
methods: "GET,POST,PUT,DELETE"
|
||||||
|
credentials: true
|
||||||
|
https:
|
||||||
|
enabled: true
|
||||||
|
certSecret: "your-tls-secret"
|
||||||
|
logging:
|
||||||
|
level: "info"
|
||||||
|
format: "json"
|
||||||
|
monitoring:
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: "/metrics"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: registry.liquidrinu.com/fusero-backend:latest
|
||||||
|
port: 14000
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
env:
|
||||||
|
POSTGRES_HOSTNAME: postgres-service
|
||||||
|
POSTGRES_PORT: "5432"
|
||||||
|
POSTGRES_DB: fusero-db
|
||||||
|
POSTGRES_USER: prod_admin
|
||||||
|
POSTGRES_PASSWORD: "<POSTGRES_PASSWORD>"
|
||||||
|
DEFAULT_ADMIN_USERNAME: admin
|
||||||
|
DEFAULT_ADMIN_EMAIL: admin@your-domain.com
|
||||||
|
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://your-canvas-instance/api/v1
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: registry.liquidrinu.com/fusero-frontend:latest
|
||||||
|
port: 80
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "100m"
|
||||||
|
memory: "128Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "300m"
|
||||||
|
memory: "256Mi"
|
||||||
|
env:
|
||||||
|
VITE_API_BASE_URL: https://your-domain.com
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
storage: 5Gi
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
password: "<POSTGRES_PASSWORD>"
|
||||||
|
user: "prod_admin"
|
||||||
|
dbName: "fusero-db"
|
70
chart/values.prod.yaml
Normal file
70
chart/values.prod.yaml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
global:
|
||||||
|
namespace: fusero-prod
|
||||||
|
security:
|
||||||
|
cors:
|
||||||
|
origin: "https://your-domain.com"
|
||||||
|
methods: "GET,POST,PUT,DELETE"
|
||||||
|
credentials: true
|
||||||
|
https:
|
||||||
|
enabled: true
|
||||||
|
certSecret: "your-tls-secret"
|
||||||
|
logging:
|
||||||
|
level: "info"
|
||||||
|
format: "json"
|
||||||
|
monitoring:
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
path: "/metrics"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: registry.liquidrinu.com/fusero-backend:latest
|
||||||
|
port: 14000
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
||||||
|
env:
|
||||||
|
POSTGRES_HOST: postgres-service
|
||||||
|
POSTGRES_HOSTNAME: 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
|
||||||
|
port: 80
|
||||||
|
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
|
||||||
|
dbName: fusero-db
|
||||||
|
user: prod_admin
|
||||||
|
password: ${POSTGRES_PASSWORD}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: "200m"
|
||||||
|
memory: "256Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "500m"
|
||||||
|
memory: "512Mi"
|
@ -1,22 +0,0 @@
|
|||||||
backend:
|
|
||||||
image: fusero-backend:latest
|
|
||||||
env:
|
|
||||||
POSTGRES_HOST: postgres-service
|
|
||||||
POSTGRES_PORT: "5432"
|
|
||||||
POSTGRES_NAME: fusero-db
|
|
||||||
POSTGRES_USER: prod_admin
|
|
||||||
POSTGRES_PASSWORD: REPLACE_ME
|
|
||||||
DEFAULT_ADMIN_USERNAME: admin
|
|
||||||
DEFAULT_ADMIN_EMAIL: admin@fusero.nl
|
|
||||||
DEFAULT_ADMIN_PASSWORD: STRONG_REPLACE_ME
|
|
||||||
ENCRYPTION_KEY: PROD_REPLACE_ME_KEY
|
|
||||||
JWT_SECRET: PROD_REPLACE_ME_JWT
|
|
||||||
CHATGPT_API_KEY: PROD_REPLACE_ME_CHATGPT
|
|
||||||
CANVAS_API_KEY: PROD_REPLACE_ME_CANVAS
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
image: fusero-frontend:latest
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:15
|
|
||||||
storage: 5Gi
|
|
@ -3,12 +3,21 @@ import { PostgreSqlDriver } from '@mikro-orm/postgresql';
|
|||||||
import { Migrator } from '@mikro-orm/migrations';
|
import { Migrator } from '@mikro-orm/migrations';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
// Load environment variables if not in Kubernetes
|
||||||
if (process.env.KUBERNETES_SERVICE_HOST === undefined) {
|
if (process.env.KUBERNETES_SERVICE_HOST === undefined) {
|
||||||
dotenv.config({ override: true });
|
dotenv.config({ override: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
|
// Validate required environment variables
|
||||||
|
const requiredEnvVars = ['POSTGRES_DB', 'POSTGRES_USER', 'POSTGRES_PASSWORD'];
|
||||||
|
const missingEnvVars = requiredEnvVars.filter(envVar => !process.env[envVar]);
|
||||||
|
|
||||||
|
if (missingEnvVars.length > 0 && isProduction) {
|
||||||
|
throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
const config: Options = {
|
const config: Options = {
|
||||||
driver: PostgreSqlDriver,
|
driver: PostgreSqlDriver,
|
||||||
entities: [
|
entities: [
|
||||||
@ -25,14 +34,14 @@ const config: Options = {
|
|||||||
warnWhenNoEntities: true,
|
warnWhenNoEntities: true,
|
||||||
disableDynamicFileAccess: false,
|
disableDynamicFileAccess: false,
|
||||||
},
|
},
|
||||||
dbName: process.env.POSTGRES_NAME || 'fusero-boilerplate-db',
|
dbName: process.env.POSTGRES_DB || 'fusero-boilerplate-db',
|
||||||
host: process.env.POSTGRES_HOSTNAME || 'localhost',
|
host: process.env.POSTGRES_HOSTNAME || 'localhost',
|
||||||
port: Number(process.env.POSTGRES_PORT) || 5432,
|
port: Number(process.env.POSTGRES_PORT) || 5432,
|
||||||
user: process.env.POSTGRES_USER || 'root',
|
user: process.env.POSTGRES_USER || 'root',
|
||||||
password: process.env.POSTGRES_PASSWORD || 'root123',
|
password: process.env.POSTGRES_PASSWORD || 'root123',
|
||||||
debug: !isProduction,
|
debug: !isProduction,
|
||||||
migrations: {
|
migrations: {
|
||||||
tableName: process.env.POSTGRES_NAME,
|
tableName: process.env.POSTGRES_DB,
|
||||||
path: isProduction ? './dist/src/database/migrations' : './src/database/migrations',
|
path: isProduction ? './dist/src/database/migrations' : './src/database/migrations',
|
||||||
glob: '!(*.d).{js,ts}',
|
glob: '!(*.d).{js,ts}',
|
||||||
transactional: true,
|
transactional: true,
|
||||||
@ -43,6 +52,13 @@ const config: Options = {
|
|||||||
snapshot: true,
|
snapshot: true,
|
||||||
emit: 'ts',
|
emit: 'ts',
|
||||||
},
|
},
|
||||||
|
// Add connection pool settings
|
||||||
|
pool: {
|
||||||
|
min: 2,
|
||||||
|
max: 10,
|
||||||
|
idleTimeoutMillis: 30000,
|
||||||
|
acquireTimeoutMillis: 30000,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
1
trigger.txt
Normal file
1
trigger.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
# trigger
|
Loading…
Reference in New Issue
Block a user