Compare commits
2 Commits
main
...
hotfix-pat
Author | SHA1 | Date | |
---|---|---|---|
0576193b76 | |||
c0db4dc2c9 |
@ -14,6 +14,10 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:latest
|
||||
network=host
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v2
|
||||
@ -30,11 +34,10 @@ jobs:
|
||||
tags: registry.liquidrinu.com/fusero-backend:latest
|
||||
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-backend:buildcache
|
||||
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
|
||||
build-args: |
|
||||
BUILDKIT_INLINE_CACHE=1
|
||||
platforms: linux/amd64
|
||||
compression: zstd
|
||||
|
||||
- name: Build and Push Frontend
|
||||
uses: docker/build-push-action@v4
|
||||
@ -45,6 +48,10 @@ jobs:
|
||||
tags: registry.liquidrinu.com/fusero-frontend:latest
|
||||
cache-from: type=registry,ref=registry.liquidrinu.com/fusero-frontend:buildcache
|
||||
cache-to: type=registry,ref=registry.liquidrinu.com/fusero-frontend:buildcache,mode=max
|
||||
build-args: |
|
||||
BUILDKIT_INLINE_CACHE=1
|
||||
platforms: linux/amd64
|
||||
compression: zstd
|
||||
|
||||
- name: Install kubectl
|
||||
uses: azure/setup-kubectl@v3
|
||||
@ -74,13 +81,6 @@ jobs:
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
helm upgrade --install fusero ./chart \
|
||||
@ -88,8 +88,8 @@ jobs:
|
||||
--create-namespace \
|
||||
--values ./chart/values.prod.yaml \
|
||||
--values ./chart/secrets.prod.yaml \
|
||||
--set backend.image=registry.liquidrinu.com/fusero-backend:latest \
|
||||
--set frontend.image=registry.liquidrinu.com/fusero-frontend:latest
|
||||
--set backend.image.repository=registry.liquidrinu.com/fusero-backend \
|
||||
--set frontend.image.repository=registry.liquidrinu.com/fusero-frontend
|
||||
|
||||
- name: Wait for migration/seed job
|
||||
run: |
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -143,7 +143,3 @@ chart/secrets.*.yaml
|
||||
!chart/values.prod.public.yaml
|
||||
|
||||
.bkup/
|
||||
|
||||
# Ignore production values and secrets files
|
||||
chart/values.prod.yaml
|
||||
chart/secrets.prod.yaml
|
||||
|
40
Dockerfile
40
Dockerfile
@ -1,4 +1,4 @@
|
||||
# Use Node.js 18.3 as the base image
|
||||
# Build stage
|
||||
FROM node:20-slim AS build
|
||||
|
||||
# Install Python and build tools for node-gyp
|
||||
@ -12,31 +12,53 @@ RUN apt-get update && \
|
||||
ENV APP_DIR=/usr/src/app/
|
||||
RUN mkdir -p ${APP_DIR}
|
||||
|
||||
# Install global dependencies like pm2, ts-node, and typescript as root
|
||||
# Install global dependencies
|
||||
RUN npm install -g pm2 ts-node typescript
|
||||
|
||||
# Create a non-root user and switch to it
|
||||
# Create a non-root user
|
||||
ENV APP_USER=appuser
|
||||
RUN adduser --disabled-password --gecos '' ${APP_USER}
|
||||
WORKDIR ${APP_DIR}
|
||||
RUN chown -R ${APP_USER}:${APP_USER} ${APP_DIR}
|
||||
|
||||
# Switch to non-root user before copying files and installing dependencies
|
||||
# Switch to non-root user
|
||||
USER ${APP_USER}
|
||||
|
||||
# Copy package.json and package-lock.json and install dependencies as appuser
|
||||
# Copy package files and install dependencies
|
||||
COPY --chown=${APP_USER}:${APP_USER} package.json package-lock.json ./
|
||||
RUN npm install
|
||||
|
||||
# Copy the rest of the application code with appropriate ownership
|
||||
# Copy source code
|
||||
COPY --chown=${APP_USER}:${APP_USER} . .
|
||||
|
||||
# Rebuild bcrypt and other native dependencies as appuser
|
||||
# Rebuild native dependencies
|
||||
RUN npm rebuild bcrypt --build-from-source
|
||||
|
||||
# Build the application using the npm script, assuming "build:ts" is defined
|
||||
# Build the application
|
||||
RUN npm run build:ts
|
||||
|
||||
# Production stage
|
||||
FROM node:20-slim
|
||||
|
||||
# Install only production dependencies
|
||||
ENV APP_DIR=/usr/src/app/
|
||||
RUN mkdir -p ${APP_DIR}
|
||||
|
||||
# Create non-root user
|
||||
ENV APP_USER=appuser
|
||||
RUN adduser --disabled-password --gecos '' ${APP_USER}
|
||||
WORKDIR ${APP_DIR}
|
||||
RUN chown -R ${APP_USER}:${APP_USER} ${APP_DIR}
|
||||
|
||||
# Copy only necessary files from build stage
|
||||
COPY --from=build --chown=${APP_USER}:${APP_USER} ${APP_DIR}/dist ./dist
|
||||
COPY --from=build --chown=${APP_USER}:${APP_USER} ${APP_DIR}/package.json ./
|
||||
COPY --from=build --chown=${APP_USER}:${APP_USER} ${APP_DIR}/package-lock.json ./
|
||||
|
||||
# Install only production dependencies
|
||||
USER ${APP_USER}
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Environment variables
|
||||
ENV CI=true
|
||||
ENV PORT=14000
|
||||
@ -45,5 +67,5 @@ ENV NODE_ENV=production
|
||||
# Expose the application's port
|
||||
EXPOSE ${PORT}
|
||||
|
||||
# Command to run the application using npm start
|
||||
# Command to run the application
|
||||
CMD ["npm", "start"]
|
||||
|
138
README.md
138
README.md
@ -10,9 +10,44 @@ A full-stack application boilerplate with a React frontend and Node.js backend
|
||||
- [📚 Table of Contents](#-table-of-contents)
|
||||
- [📁 Project Structure](#-project-structure)
|
||||
- [⚙️ Prerequisites](#️-prerequisites)
|
||||
- [Development Setup](#development-setup)
|
||||
- [Important Note: Database Must Run in Docker](#important-note-database-must-run-in-docker)
|
||||
- [💻 Development Setup](#-development-setup)
|
||||
- [To create a new migration:](#to-create-a-new-migration)
|
||||
- [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)
|
||||
- [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)
|
||||
@ -47,17 +82,12 @@ A full-stack application boilerplate with a React frontend and Node.js backend
|
||||
- [Troubleshooting Production](#troubleshooting-production)
|
||||
- [🆕 Recent Improvements \& Troubleshooting](#-recent-improvements--troubleshooting)
|
||||
- [🚀 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
|
||||
|
||||
```
|
||||
fusero-app-boilerplate
|
||||
fusero-app-boilerplate/
|
||||
├── chart/ # Helm chart for Kubernetes
|
||||
│ ├── Chart.yaml
|
||||
│ ├── values.dev.yaml
|
||||
@ -95,7 +125,6 @@ fusero-app-boilerplate
|
||||
├── test/
|
||||
├── utils/
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@ -528,94 +557,3 @@ 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.
|
||||
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
6
chart/Chart.yml
Normal file
6
chart/Chart.yml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: fusero
|
||||
description: Fusero App Boilerplate Helm Chart
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "1.0.0"
|
@ -12,8 +12,6 @@ spec:
|
||||
labels:
|
||||
app: fusero-backend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: backend
|
||||
image: {{ .Values.backend.image }}
|
||||
|
@ -8,62 +8,18 @@ spec:
|
||||
metadata:
|
||||
name: fusero-backend-db-init
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: migrate-seed
|
||||
image: {{ .Values.backend.image }}
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
echo "=== Environment Variables ==="
|
||||
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 ==="
|
||||
echo "Running migrations and seeds..." && \
|
||||
npx mikro-orm migration:up && \
|
||||
npm run seed
|
||||
env:
|
||||
- name: POSTGRES_HOSTNAME
|
||||
value: "postgres-service"
|
||||
- name: POSTGRES_PORT
|
||||
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 }}"
|
||||
{{- range $key, $val := .Values.backend.env }}
|
||||
- name: {{ $key }}
|
||||
value: "{{ $val }}"
|
||||
{{- end }}
|
||||
restartPolicy: Never
|
||||
|
@ -12,8 +12,6 @@ spec:
|
||||
labels:
|
||||
app: fusero-frontend
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: regcred
|
||||
containers:
|
||||
- name: frontend
|
||||
image: {{ .Values.frontend.image }}
|
||||
|
@ -8,51 +8,11 @@ data:
|
||||
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 scram-sha-256
|
||||
host all all 0.0.0.0/0 md5
|
||||
postgresql.conf: |
|
||||
# Connection Settings
|
||||
listen_addresses = '*'
|
||||
max_connections = 100
|
||||
|
||||
# Memory Settings
|
||||
shared_buffers = 128MB
|
||||
work_mem = 4MB
|
||||
maintenance_work_mem = 64MB
|
||||
|
||||
# Write Ahead Log
|
||||
dynamic_shared_memory_type = posix
|
||||
max_wal_size = 1GB
|
||||
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,42 +19,11 @@ spec:
|
||||
- containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: "{{ .Values.postgres.dbName }}"
|
||||
value: {{ .Values.postgres.dbName }}
|
||||
- name: POSTGRES_USER
|
||||
value: "{{ .Values.postgres.user }}"
|
||||
value: {{ .Values.postgres.user }}
|
||||
- name: 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
|
||||
value: {{ .Values.postgres.password }}
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/postgresql/data
|
||||
name: postgres-data
|
||||
|
@ -1,69 +0,0 @@
|
||||
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"
|
@ -1,70 +0,0 @@
|
||||
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"
|
22
chart/values.prod.yml
Normal file
22
chart/values.prod.yml
Normal file
@ -0,0 +1,22 @@
|
||||
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
|
@ -1,10 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "ES2015"],
|
||||
"lib": [
|
||||
"DOM",
|
||||
"DOM.Iterable",
|
||||
"ESNext",
|
||||
"ES2015"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
@ -12,7 +16,6 @@
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": false,
|
||||
"noUnusedLocals": false,
|
||||
@ -22,7 +25,6 @@
|
||||
"noImplicitReturns": false,
|
||||
"noImplicitThis": false,
|
||||
"alwaysStrict": false
|
||||
|
||||
/* Additional Options */
|
||||
// "forceConsistentCasingInFileNames": true,
|
||||
// "strictNullChecks": true,
|
||||
@ -32,6 +34,12 @@
|
||||
// "incremental": true,
|
||||
// "esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
@ -3,21 +3,12 @@ import { PostgreSqlDriver } from '@mikro-orm/postgresql';
|
||||
import { Migrator } from '@mikro-orm/migrations';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Load environment variables if not in Kubernetes
|
||||
if (process.env.KUBERNETES_SERVICE_HOST === undefined) {
|
||||
dotenv.config({ override: true });
|
||||
}
|
||||
|
||||
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 = {
|
||||
driver: PostgreSqlDriver,
|
||||
entities: [
|
||||
@ -34,14 +25,14 @@ const config: Options = {
|
||||
warnWhenNoEntities: true,
|
||||
disableDynamicFileAccess: false,
|
||||
},
|
||||
dbName: process.env.POSTGRES_DB || 'fusero-boilerplate-db',
|
||||
dbName: process.env.POSTGRES_NAME || 'fusero-boilerplate-db',
|
||||
host: process.env.POSTGRES_HOSTNAME || 'localhost',
|
||||
port: Number(process.env.POSTGRES_PORT) || 5432,
|
||||
user: process.env.POSTGRES_USER || 'root',
|
||||
password: process.env.POSTGRES_PASSWORD || 'root123',
|
||||
debug: !isProduction,
|
||||
migrations: {
|
||||
tableName: process.env.POSTGRES_DB,
|
||||
tableName: process.env.POSTGRES_NAME,
|
||||
path: isProduction ? './dist/src/database/migrations' : './src/database/migrations',
|
||||
glob: '!(*.d).{js,ts}',
|
||||
transactional: true,
|
||||
@ -52,13 +43,6 @@ const config: Options = {
|
||||
snapshot: true,
|
||||
emit: 'ts',
|
||||
},
|
||||
// Add connection pool settings
|
||||
pool: {
|
||||
min: 2,
|
||||
max: 10,
|
||||
idleTimeoutMillis: 30000,
|
||||
acquireTimeoutMillis: 30000,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
@ -1 +0,0 @@
|
||||
# trigger
|
Loading…
Reference in New Issue
Block a user