Compare commits

..

No commits in common. "0c9f5b58e031c48a7e3fd09b4b6ba8657427db12" and "20d0993b06bbdc276cf80d4b0b80b381674ba52e" have entirely different histories.

16 changed files with 87 additions and 290 deletions

View File

@ -39,18 +39,12 @@ jobs:
- name: Login to Container Registry
run: echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USER" --password-stdin
- name: Build and Push API image
- name: Build and Push Docker image
run: |
docker build -f Dockerfile.api -t "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee:latest" .
docker build -t "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee:latest" .
docker push "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG"
docker push "$REGISTRY_URL/sortifal/pfee:latest"
- name: Build and Push Frontend image
run: |
docker build -f Dockerfile.frontend -t "$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee-frontend:latest" .
docker push "$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG"
docker push "$REGISTRY_URL/sortifal/pfee-frontend:latest"
deploy:
name: Deploy to Kubernetes
runs-on: ubuntu-latest
@ -81,20 +75,12 @@ jobs:
-n sqdc-dashboard \
--dry-run=client -o yaml | kubectl apply -f -
- name: Install Kustomize
- name: Deploy to Kubernetes
run: |
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
kubectl apply -f k8s/namespace.yaml k8s/deployment.yaml k8s/service.yaml k8s/ingress.yaml
- name: Deploy with Kustomize
- name: Update deployment and verify
run: |
cd k8s
kustomize edit set image gitea.vidoks.fr/sortifal/pfee="$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG"
kustomize edit set image gitea.vidoks.fr/sortifal/pfee-frontend="$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG"
kubectl apply -k .
- name: Verify deployment
run: |
kubectl rollout status deployment/sqdc-api -n sqdc-dashboard --timeout=5m
kubectl rollout status deployment/sqdc-frontend -n sqdc-dashboard --timeout=5m
kubectl set image deployment/sqdc-dashboard dashboard="$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -n sqdc-dashboard
kubectl rollout status deployment/sqdc-dashboard -n sqdc-dashboard --timeout=5m
kubectl get pods,svc,ingress -n sqdc-dashboard

View File

@ -1,4 +1,5 @@
node_modules
build
.git
.gitignore
*.md

View File

@ -8,8 +8,8 @@ WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies (including dev) needed for build
RUN npm ci
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
@ -17,9 +17,6 @@ COPY . .
# Build the application
RUN npm run build
# Install only production dependencies for runtime
RUN npm ci --only=production
# Stage 2: Production image with Nginx
FROM nginx:alpine
@ -46,4 +43,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
# Start both nginx and the API server
CMD ["sh", "-c", "node /app/server.js & nginx -g 'daemon off;'"]
CMD sh -c "node /app/server.js & nginx -g 'daemon off;'"

View File

@ -1,24 +0,0 @@
# API Server Dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production
# Copy database and server files
COPY database ./database
COPY server.js .
# Expose API port
EXPOSE 3001
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/api/categories', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
# Start the API server
CMD ["node", "server.js"]

View File

@ -1,35 +0,0 @@
# Multi-stage build for React frontend with Nginx
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies (including dev) needed for build
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build
# Stage 2: Production image with Nginx
FROM nginx:alpine
# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copy built application from builder stage
COPY --from=builder /app/build /usr/share/nginx/html
# Expose port
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: sqdc-api
namespace: sqdc-dashboard
labels:
app: sqdc-api
spec:
type: ClusterIP
ports:
- port: 3001
targetPort: 3001
protocol: TCP
name: api
selector:
app: sqdc-api

View File

@ -1,58 +0,0 @@
apiVersion: batch/v1
kind: Job
metadata:
name: sqdc-db-init
namespace: sqdc-dashboard
labels:
app: sqdc-api
spec:
backoffLimit: 3
template:
metadata:
labels:
app: sqdc-api-init
spec:
serviceAccountName: sqdc-db-init
restartPolicy: Never
containers:
- name: db-init
image: gitea.vidoks.fr/sortifal/pfee:latest
imagePullPolicy: Always
command:
- sh
- -c
- |
echo "Starting database initialization..."
if [ ! -f /app/database/sqdc.db ]; then
echo "Creating new database from schema..."
sqlite3 /app/database/sqdc.db < /app/database/schema.sql
echo "Populating database with sample data..."
python3 /app/database/populate_db.py
echo "✅ Database initialized successfully"
else
echo "✅ Database already exists, skipping initialization"
fi
echo "Verifying database integrity..."
sqlite3 /app/database/sqdc.db "SELECT COUNT(*) as table_count FROM sqlite_master WHERE type='table';"
echo "Database initialization complete"
volumeMounts:
- name: database
mountPath: /app/database
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
volumes:
- name: database
persistentVolumeClaim:
claimName: sqdc-database-pvc
imagePullSecrets:
- name: registry-credentials

View File

@ -1,7 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: sqdc-db-init
namespace: sqdc-dashboard
labels:
app: sqdc-api

View File

@ -1,25 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sqdc-api
name: sqdc-dashboard
namespace: sqdc-dashboard
labels:
app: sqdc-api
app: sqdc-dashboard
spec:
replicas: 2
selector:
matchLabels:
app: sqdc-api
app: sqdc-dashboard
template:
metadata:
labels:
app: sqdc-api
app: sqdc-dashboard
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: api
- name: dashboard
image: gitea.vidoks.fr/sortifal/pfee:latest
imagePullPolicy: Always
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 3001
name: api
protocol: TCP
@ -28,23 +33,23 @@ spec:
value: "production"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/categories
port: 3001
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/categories
port: 3001
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
@ -55,6 +60,17 @@ spec:
volumes:
- name: database
persistentVolumeClaim:
claimName: sqdc-database-pvc
imagePullSecrets:
- name: registry-credentials
claimName: sqdc-dashboard-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sqdc-dashboard-pvc
namespace: sqdc-dashboard
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard

View File

@ -1,50 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sqdc-frontend
namespace: sqdc-dashboard
labels:
app: sqdc-frontend
spec:
replicas: 2
selector:
matchLabels:
app: sqdc-frontend
template:
metadata:
labels:
app: sqdc-frontend
spec:
containers:
- name: frontend
image: gitea.vidoks.fr/sortifal/pfee-frontend:latest
imagePullPolicy: Always
ports:
- containerPort: 80
name: http
protocol: TCP
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
imagePullSecrets:
- name: registry-credentials

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: sqdc-frontend
namespace: sqdc-dashboard
labels:
app: sqdc-frontend
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: sqdc-frontend

View File

@ -4,26 +4,18 @@ metadata:
name: sqdc-dashboard-ingress
namespace: sqdc-dashboard
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: diwii.sortifal.dev
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: sqdc-api
port:
number: 3001
- path: /
pathType: Prefix
backend:
service:
name: sqdc-frontend
name: sqdc-dashboard-service
port:
number: 80

View File

@ -5,24 +5,15 @@ namespace: sqdc-dashboard
resources:
- namespace.yaml
- pvc.yaml
- db-init-sa.yaml
- db-init-job.yaml
- api-deployment.yaml
- api-service.yaml
- frontend-deployment.yaml
- frontend-service.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
- configmap.yaml
commonLabels:
app: sqdc-dashboard
managed-by: kustomize
# Image tags will be set via kustomize edit during deployment
images:
- name: gitea.vidoks.fr/sortifal/pfee
newTag: latest
newName: gitea.vidoks.fr/sortifal/pfee
- name: gitea.vidoks.fr/sortifal/pfee-frontend
newTag: latest
newName: gitea.vidoks.fr/sortifal/pfee-frontend

View File

@ -1,13 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sqdc-database-pvc
namespace: sqdc-dashboard
labels:
app: sqdc-api
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

View File

@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: sqdc-dashboard-service
namespace: sqdc-dashboard
labels:
app: sqdc-dashboard
spec:
type: ClusterIP
selector:
app: sqdc-dashboard
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: api
port: 3001
targetPort: 3001
protocol: TCP

View File

@ -1,8 +1,8 @@
server {
listen 80;
server_name _;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
index index.html;
# Gzip compression
gzip on;
@ -10,13 +10,20 @@ server {
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
# Static assets with caching
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
# API proxy
location /api/ {
proxy_pass http://localhost:3001/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# React app - handle client-side routing (must be last)
# React app - handle client-side routing
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
@ -24,6 +31,12 @@ server {
add_header Expires "0";
}
# Static assets with caching
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;