apiVersion: apps/v1
kind: Deployment
metadata:
  name: savepal-backend
  namespace: savepal-production
  labels:
    app: savepal-backend
    component: api
    environment: production
    version: "1.0.0"
  annotations:
    deployment.kubernetes.io/revision: "1"
    description: "SavePal AI Personal Shopper Backend API"
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: savepal-backend
      component: api
      environment: production
  template:
    metadata:
      labels:
        app: savepal-backend
        component: api
        environment: production
        version: "1.0.0"
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "4100"
        prometheus.io/path: "/metrics"
    spec:
      serviceAccountName: savepal-backend-sa
      imagePullSecrets:
        - name: savepal-registry-secret
      containers:
      - name: savepal-backend
        image: savepal/backend:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 4100
          name: http
          protocol: TCP
        env:
        - name: NODE_ENV
          valueFrom:
            configMapKeyRef:
              name: savepal-backend-config
              key: NODE_ENV
        - name: PORT
          valueFrom:
            configMapKeyRef:
              name: savepal-backend-config
              key: PORT
        - name: MONGODB_URI
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: MONGODB_URI
        - name: REDIS_HOST
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: REDIS_HOST
        - name: REDIS_PORT
          valueFrom:
            configMapKeyRef:
              name: savepal-backend-config
              key: REDIS_PORT
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: REDIS_PASSWORD
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: JWT_SECRET
        - name: JWT_REFRESH_SECRET
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: JWT_REFRESH_SECRET
        - name: AWS_ACCESS_KEY_ID
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: AWS_ACCESS_KEY_ID
        - name: AWS_SECRET_ACCESS_KEY
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: AWS_SECRET_ACCESS_KEY
        - name: AWS_REGION
          valueFrom:
            configMapKeyRef:
              name: savepal-backend-config
              key: AWS_REGION
        - name: AWS_S3_BUCKET
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: AWS_S3_BUCKET
        - name: HUGGINGFACE_API_KEY
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: HUGGINGFACE_API_KEY
        - name: OPENROUTER_API_KEY
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: OPENROUTER_API_KEY
        - name: ANTHROPIC_API_KEY
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: ANTHROPIC_API_KEY
        - name: SENTRY_DSN
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: SENTRY_DSN
        - name: DATADOG_API_KEY
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: DATADOG_API_KEY
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 4100
            scheme: HTTP
          initialDelaySeconds: 30
          periodSeconds: 30
          timeoutSeconds: 10
          failureThreshold: 3
          successThreshold: 1
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 4100
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
          successThreshold: 1
        startupProbe:
          httpGet:
            path: /health
            port: 4100
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 30
          successThreshold: 1
        volumeMounts:
        - name: logs
          mountPath: /app/logs
        - name: uploads
          mountPath: /app/uploads
        - name: config
          mountPath: /app/config/production
          readOnly: true
        securityContext:
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1001
          runAsGroup: 1001
          readOnlyRootFilesystem: false
          capabilities:
            drop:
            - ALL
      volumes:
      - name: logs
        emptyDir:
          sizeLimit: 1Gi
      - name: uploads
        emptyDir:
          sizeLimit: 5Gi
      - name: config
        configMap:
          name: savepal-backend-config
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      securityContext:
        fsGroup: 1001
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - savepal-backend
              topologyKey: kubernetes.io/hostname
      tolerations:
      - key: "node.kubernetes.io/not-ready"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 300
      - key: "node.kubernetes.io/unreachable"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 300

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: savepal-redis
  namespace: savepal-production
  labels:
    app: savepal-redis
    component: cache
    environment: production
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: savepal-redis
      component: cache
      environment: production
  template:
    metadata:
      labels:
        app: savepal-redis
        component: cache
        environment: production
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        ports:
        - containerPort: 6379
          name: redis
          protocol: TCP
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: savepal-backend-secrets
              key: REDIS_PASSWORD
        command:
        - redis-server
        - --appendonly
        - "yes"
        - --requirepass
        - $(REDIS_PASSWORD)
        - --maxmemory
        - "1gb"
        - --maxmemory-policy
        - "allkeys-lru"
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          exec:
            command:
            - redis-cli
            - --raw
            - incr
            - ping
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          exec:
            command:
            - redis-cli
            - --raw
            - incr
            - ping
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
        volumeMounts:
        - name: redis-data
          mountPath: /data
        securityContext:
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 999
          runAsGroup: 999
          readOnlyRootFilesystem: false
          capabilities:
            drop:
            - ALL
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: savepal-redis-pvc
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      securityContext:
        fsGroup: 999
        runAsNonRoot: true
