William Valentin 0ea1af91c9 feat: Complete Kustomize migration with environment variable integration
🎉 Major enhancement: Full migration from shell script deployment to Kustomize

## New Features

### Kustomize Infrastructure
-  Complete base resources for all Kubernetes manifests
-  Development overlay with optimized dev settings
-  Production overlay with enterprise-grade security and performance
-  ConfigMap and Secret generation from environment variables
-  Image tag and replica management per environment

### Environment Variable Integration
-  Multi-source environment loading (~/.env, .env.dev, .env.prod, .env.local)
-  Static configuration generation from environment variables
-  Dynamic runtime environment variable injection
-  Comprehensive variable documentation and examples
-  Secrets template generation for secure credential management

### Enhanced Makefile
-  20+ new Kustomize-specific deployment targets
-  Environment-aware configuration generation commands
-  Validation, dry-run, and debugging capabilities
-  Backward compatibility with legacy shell script deployment

### New Scripts & Tools
-  scripts/generate-config.sh - Environment variable to Kustomize config generator
-  scripts/deploy-with-env.sh - Runtime environment variable deployment tool
-  Comprehensive help and usage documentation

### Documentation
-  k8s-kustomize/README.md - Complete Kustomize deployment guide
-  docs/ENVIRONMENT_VARIABLES.md - Environment variable integration guide
-  KUSTOMIZE_MIGRATION.md - Migration summary and next steps

## Benefits
- 🚀 Simplified deployment: make deploy-dev vs complex shell scripts
- 🔒 Environment isolation: Clear dev/staging/prod separation
- 🔧 GitOps ready: Works seamlessly with ArgoCD, Flux
-  Better validation: Built-in YAML validation catches errors early
- 📈 Standard approach: Industry-standard Kubernetes deployment method
- 🛡️ Enhanced security: Production security contexts, network policies, TLS

## Usage Examples
Generating development configuration...
[INFO] Kustomize Config Generator
[INFO] Environment: dev
[INFO] Loading environment variables...
[WARNING] File not found: /home/will/.env
[INFO] Loading: /home/will/Code/meds/.env
[WARNING] File not found: /home/will/Code/meds/.env.dev
[WARNING] File not found: /home/will/Code/meds/.env.local
[INFO] Generating base config.env...
[SUCCESS] Generated: /home/will/Code/meds/k8s-kustomize/base/config.env
[INFO] Generating environment-specific config for: dev
[SUCCESS] Generated development config: /home/will/Code/meds/k8s-kustomize/overlays/dev/config.env
[INFO] Validating generated configuration...
[SUCCESS] Configuration validation passed!
[SUCCESS] Configuration generation completed!
[INFO] Next steps:
  1. Review generated files in k8s-kustomize/
  2. Update any environment-specific values
  3. Create secrets.env files for sensitive data
  4. Test with: make kustomize-dry-run-dev
Deploying to Kubernetes with Kustomize (dev)...
Deploying to production with environment variables...
[INFO] Kustomize Deployment with Environment Variables
[INFO] Environment: prod
[INFO] Action: apply
[INFO] Validating prerequisites...
[SUCCESS] Prerequisites validated
[INFO] Loading environment variables for: prod
[INFO] Loading: /home/will/Code/meds/.env
[SUCCESS] Environment loaded: prod
[INFO] Key variables:
  APP_NAME: rxminder
  NODE_ENV: production
  IMAGE_TAG: latest
  NAMESPACE: rxminder-prod
  INGRESS_HOST: rxminder.192.168.153.243.nip.io
[INFO] Generating dynamic configuration...
Validating Kustomize configuration (dev)...
configmap/rxminder-config-4229dg76t6 created (dry run)
secret/couchdb-secret-7ck2cc96g5 created (dry run)
service/rxminder-couchdb-service created (dry run)
service/rxminder-frontend-service created (dry run)
persistentvolumeclaim/rxminder-couchdb-pvc created (dry run)
deployment.apps/rxminder-frontend created (dry run)
statefulset.apps/rxminder-couchdb created (dry run)
horizontalpodautoscaler.autoscaling/rxminder-frontend-hpa created (dry run)
job.batch/rxminder-db-seed created (dry run)
ingress.networking.k8s.io/rxminder-ingress created (dry run)
networkpolicy.networking.k8s.io/rxminder-database-policy created (dry run)
networkpolicy.networking.k8s.io/rxminder-frontend-policy created (dry run)
Validating Kustomize configuration (prod)...
configmap/rxminder-config-2979gkcf9c created (dry run)
secret/couchdb-secret-6k9794bgg2 created (dry run)
service/rxminder-couchdb-service created (dry run)
service/rxminder-frontend-service created (dry run)
persistentvolumeclaim/rxminder-couchdb-pvc created (dry run)
deployment.apps/rxminder-frontend created (dry run)
statefulset.apps/rxminder-couchdb created (dry run)
horizontalpodautoscaler.autoscaling/rxminder-frontend-hpa created (dry run)
job.batch/rxminder-db-seed created (dry run)
ingress.networking.k8s.io/rxminder-ingress created (dry run)
networkpolicy.networking.k8s.io/rxminder-database-policy created (dry run)
networkpolicy.networking.k8s.io/rxminder-frontend-policy created (dry run)
Kustomize validation completed!
Dry run Kustomize deployment (dev)...
apiVersion: v1
items:
- apiVersion: v1
  data:
    APP_NAME: rxminder
    APP_VERSION: 1.0.0
    CACHE_TTL: "1800"
    CERT_MANAGER_ISSUER: letsencrypt-prod
    CORS_ORIGIN: '*'
    COUCHDB_DATABASE_NAME: meds_app
    DB_HOST: rxminder-couchdb-service
    DB_PORT: "5984"
    DEBUG: "true"
    DEV_MODE: "false"
    ENABLE_CORS: "true"
    ENABLE_METRICS: "false"
    ENABLE_MONITORING: "false"
    ENABLE_TRACING: "false"
    HEALTH_CHECK_INTERVAL: "30"
    HOT_RELOAD: "false"
    IMAGE_REPOSITORY: will/rxminder
    INGRESS_CLASS: nginx
    LOG_FORMAT: json
    LOG_LEVEL: debug
    LOG_TIMESTAMP: "true"
    MAX_CONNECTIONS: "100"
    METRICS_PORT: "9090"
    NODE_ENV: development
    REACT_APP_API_URL: http://rxminder-couchdb-service:5984
    READINESS_CHECK_TIMEOUT: "5"
    REGISTRY_URL: gitea-http.taildb3494.ts.net
    REQUEST_TIMEOUT: "30000"
  kind: ConfigMap
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","data":{"APP_NAME":"rxminder","APP_VERSION":"1.0.0","CACHE_TTL":"1800","CERT_MANAGER_ISSUER":"letsencrypt-prod","CORS_ORIGIN":"*","COUCHDB_DATABASE_NAME":"meds_app","DB_HOST":"rxminder-couchdb-service","DB_PORT":"5984","DEBUG":"true","DEV_MODE":"false","ENABLE_CORS":"true","ENABLE_METRICS":"false","ENABLE_MONITORING":"false","ENABLE_TRACING":"false","HEALTH_CHECK_INTERVAL":"30","HOT_RELOAD":"false","IMAGE_REPOSITORY":"will/rxminder","INGRESS_CLASS":"nginx","LOG_FORMAT":"json","LOG_LEVEL":"debug","LOG_TIMESTAMP":"true","MAX_CONNECTIONS":"100","METRICS_PORT":"9090","NODE_ENV":"development","REACT_APP_API_URL":"http://rxminder-couchdb-service:5984","READINESS_CHECK_TIMEOUT":"5","REGISTRY_URL":"gitea-http.taildb3494.ts.net","REQUEST_TIMEOUT":"30000"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"app":"rxminder","environment":"dev","version":"v1.0.0"},"name":"rxminder-config-4229dg76t6","namespace":"rxminder-dev"}}
    labels:
      app: rxminder
      environment: dev
      version: v1.0.0
    name: rxminder-config-4229dg76t6
    namespace: rxminder-dev
- apiVersion: v1
  data:
    password: ZGV2cGFzczEyMw==
    username: YWRtaW4=
  kind: Secret
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","data":{"password":"ZGV2cGFzczEyMw==","username":"YWRtaW4="},"kind":"Secret","metadata":{"annotations":{},"labels":{"app":"rxminder","environment":"dev","version":"v1.0.0"},"name":"couchdb-secret-7ck2cc96g5","namespace":"rxminder-dev"},"type":"Opaque"}
    labels:
      app: rxminder
      environment: dev
      version: v1.0.0
    name: couchdb-secret-7ck2cc96g5
    namespace: rxminder-dev
  type: Opaque
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb-service","namespace":"rxminder-dev"},"spec":{"ports":[{"name":"couchdb","port":5984,"protocol":"TCP","targetPort":5984}],"selector":{"app":"rxminder","component":"database"},"type":"ClusterIP"}}
    labels:
      app: rxminder
      component: database
      environment: dev
      version: v1.0.0
    name: rxminder-couchdb-service
    namespace: rxminder-dev
  spec:
    ports:
    - name: couchdb
      port: 5984
      protocol: TCP
      targetPort: 5984
    selector:
      app: rxminder
      component: database
    type: ClusterIP
- apiVersion: v1
  kind: Service
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-service","namespace":"rxminder-dev"},"spec":{"ports":[{"name":"http","port":80,"protocol":"TCP","targetPort":80}],"selector":{"app":"rxminder","component":"frontend"},"type":"ClusterIP"}}
    labels:
      app: rxminder
      component: frontend
      environment: dev
      version: v1.0.0
    name: rxminder-frontend-service
    namespace: rxminder-dev
  spec:
    ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
    selector:
      app: rxminder
      component: frontend
    type: ClusterIP
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb-pvc","namespace":"rxminder-dev"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"1Gi"}},"storageClassName":"standard"}}
    labels:
      app: rxminder
      component: database
      environment: dev
      version: v1.0.0
    name: rxminder-couchdb-pvc
    namespace: rxminder-dev
  spec:
    accessModes:
    - ReadWriteOnce
    resources:
      requests:
        storage: 1Gi
    storageClassName: standard
- apiVersion: apps/v1
  kind: Deployment
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend","namespace":"rxminder-dev"},"spec":{"replicas":1,"selector":{"matchLabels":{"component":"frontend"}},"template":{"metadata":{"labels":{"component":"frontend"}},"spec":{"containers":[{"env":[{"name":"NODE_ENV","value":"development"},{"name":"LOG_LEVEL","value":"debug"}],"envFrom":[{"configMapRef":{"name":"rxminder-config-4229dg76t6"}}],"image":"gitea-http.taildb3494.ts.net/will/rxminder:dev","livenessProbe":{"httpGet":{"path":"/","port":80},"initialDelaySeconds":30,"periodSeconds":30},"name":"frontend","ports":[{"containerPort":80}],"readinessProbe":{"httpGet":{"path":"/","port":80},"initialDelaySeconds":5,"periodSeconds":5},"resources":{"limits":{"cpu":"40m","memory":"32Mi"},"requests":{"cpu":"20m","memory":"16Mi"}}}],"imagePullSecrets":[{"name":"rxminder-registry-secret"}]}}}}
    labels:
      app: rxminder
      component: frontend
      environment: dev
      version: v1.0.0
    name: rxminder-frontend
    namespace: rxminder-dev
  spec:
    replicas: 1
    selector:
      matchLabels:
        component: frontend
    template:
      metadata:
        labels:
          component: frontend
      spec:
        containers:
        - env:
          - name: NODE_ENV
            value: development
          - name: LOG_LEVEL
            value: debug
          envFrom:
          - configMapRef:
              name: rxminder-config-4229dg76t6
          image: gitea-http.taildb3494.ts.net/will/rxminder:dev
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 30
            periodSeconds: 30
          name: frontend
          ports:
          - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            limits:
              cpu: 40m
              memory: 32Mi
            requests:
              cpu: 20m
              memory: 16Mi
        imagePullSecrets:
        - name: rxminder-registry-secret
- apiVersion: apps/v1
  kind: StatefulSet
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-couchdb","namespace":"rxminder-dev"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"rxminder","component":"database"}},"serviceName":"rxminder-couchdb-service","template":{"metadata":{"labels":{"app":"rxminder","component":"database"}},"spec":{"containers":[{"env":[{"name":"COUCHDB_USER","valueFrom":{"secretKeyRef":{"key":"username","name":"couchdb-secret-7ck2cc96g5"}}},{"name":"COUCHDB_PASSWORD","valueFrom":{"secretKeyRef":{"key":"password","name":"couchdb-secret-7ck2cc96g5"}}}],"image":"couchdb:3.3.2","livenessProbe":{"httpGet":{"path":"/_up","port":5984},"initialDelaySeconds":60,"periodSeconds":30},"name":"couchdb","ports":[{"containerPort":5984}],"readinessProbe":{"httpGet":{"path":"/_up","port":5984},"initialDelaySeconds":10,"periodSeconds":5},"resources":{"limits":{"cpu":"60m","memory":"128Mi"},"requests":{"cpu":"30m","memory":"64Mi"}},"volumeMounts":[{"mountPath":"/opt/couchdb/data","name":"couchdb-data"}]}]}},"volumeClaimTemplates":[{"metadata":{"labels":{"app":"rxminder","component":"database"},"name":"couchdb-data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"1Gi"}},"storageClassName":"standard"}}]}}
    labels:
      app: rxminder
      component: database
      environment: dev
      version: v1.0.0
    name: rxminder-couchdb
    namespace: rxminder-dev
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: rxminder
        component: database
    serviceName: rxminder-couchdb-service
    template:
      metadata:
        labels:
          app: rxminder
          component: database
      spec:
        containers:
        - env:
          - name: COUCHDB_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: couchdb-secret-7ck2cc96g5
          - name: COUCHDB_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: couchdb-secret-7ck2cc96g5
          image: couchdb:3.3.2
          livenessProbe:
            httpGet:
              path: /_up
              port: 5984
            initialDelaySeconds: 60
            periodSeconds: 30
          name: couchdb
          ports:
          - containerPort: 5984
          readinessProbe:
            httpGet:
              path: /_up
              port: 5984
            initialDelaySeconds: 10
            periodSeconds: 5
          resources:
            limits:
              cpu: 60m
              memory: 128Mi
            requests:
              cpu: 30m
              memory: 64Mi
          volumeMounts:
          - mountPath: /opt/couchdb/data
            name: couchdb-data
    volumeClaimTemplates:
    - metadata:
        labels:
          app: rxminder
          component: database
        name: couchdb-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
        storageClassName: standard
- apiVersion: autoscaling/v2
  kind: HorizontalPodAutoscaler
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"autoscaling/v2","kind":"HorizontalPodAutoscaler","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-hpa","namespace":"rxminder-dev"},"spec":{"maxReplicas":3,"metrics":[{"resource":{"name":"cpu","target":{"averageUtilization":50,"type":"Utilization"}},"type":"Resource"}],"minReplicas":1,"scaleTargetRef":{"apiVersion":"apps/v1","kind":"Deployment","name":"rxminder-frontend"}}}
    labels:
      app: rxminder
      component: frontend
      environment: dev
      version: v1.0.0
    name: rxminder-frontend-hpa
    namespace: rxminder-dev
  spec:
    maxReplicas: 3
    metrics:
    - resource:
        name: cpu
        target:
          averageUtilization: 50
          type: Utilization
      type: Resource
    minReplicas: 1
    scaleTargetRef:
      apiVersion: apps/v1
      kind: Deployment
      name: rxminder-frontend
- apiVersion: batch/v1
  kind: Job
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-db-seed","namespace":"rxminder-dev"},"spec":{"backoffLimit":4,"template":{"metadata":{"labels":{"app":"rxminder","component":"database"}},"spec":{"containers":[{"args":["# Wait for CouchDB to be ready\necho \"Waiting for CouchDB to be ready...\"\nuntil curl -f http://couchdb-service:5984/_up 2\u003e/dev/null; do\n  sleep 2\ndone\n\n# Create databases\necho \"Creating databases...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app\n\n# Create default admin user\necho \"Creating default admin user...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/_users/org.couchdb.user:$COUCHDB_USER \\\n  -H \"Content-Type: application/json\" \\\n  -d \"{\n    \\\"name\\\": \\\"$COUCHDB_USER\\\",\n    \\\"password\\\": \\\"$COUCHDB_PASSWORD\\\",\n    \\\"roles\\\": [\\\"admin\\\"],\n    \\\"type\\\": \\\"user\\\"\n  }\"\n\n# Create design documents for views\necho \"Creating design documents...\"\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/medications \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"views\": {\n      \"by_name\": {\n        \"map\": \"function(doc) { if (doc.type === \\\"medication\\\") emit(doc.name, doc); }\"\n      },\n      \"by_user\": {\n        \"map\": \"function(doc) { if (doc.type === \\\"medication\\\") emit(doc.userId, doc); }\"\n      }\n    }\n  }'\n\ncurl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/reminders \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"views\": {\n      \"by_medication\": {\n        \"map\": \"function(doc) { if (doc.type === \\\"reminder\\\") emit(doc.medicationId, doc); }\"\n      },\n      \"by_user\": {\n        \"map\": \"function(doc) { if (doc.type === \\\"reminder\\\") emit(doc.userId, doc); }\"\n      }\n    }\n  }'\n\n# Create a sample user document for reference\n  # Create design document for authentication users\n  curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/auth \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\n      \"views\": {\n        \"by_username\": {\n          \"map\": \"function(doc) { if (doc.type === \\\"user\\\" \u0026\u0026 doc.username) emit(doc.username, doc); }\"\n        },\n        \"by_email\": {\n          \"map\": \"function(doc) { if (doc.type === \\\"user\\\" \u0026\u0026 doc.email) emit(doc.email, doc); }\"\n        }\n      }\n    }'\necho \"Creating sample user document...\"\ncurl -X POST http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"type\": \"user\",\n    \"name\": \"sample_user\",\n    \"email\": \"user@example.com\",\n    \"createdAt\": \"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'\"\n  }'\n\necho \"Database seeding completed with default admin user\"\n"],"command":["/bin/sh","-c"],"env":[{"name":"COUCHDB_USER","valueFrom":{"secretKeyRef":{"key":"username","name":"couchdb-secret-7ck2cc96g5"}}},{"name":"COUCHDB_PASSWORD","valueFrom":{"secretKeyRef":{"key":"password","name":"couchdb-secret-7ck2cc96g5"}}}],"image":"couchdb:3.3.2","name":"db-seeder"}],"restartPolicy":"Never"}}}}
    labels:
      app: rxminder
      component: database
      environment: dev
      version: v1.0.0
    name: rxminder-db-seed
    namespace: rxminder-dev
  spec:
    backoffLimit: 4
    template:
      metadata:
        labels:
          app: rxminder
          component: database
      spec:
        containers:
        - args:
          - |
            # Wait for CouchDB to be ready
            echo "Waiting for CouchDB to be ready..."
            until curl -f http://couchdb-service:5984/_up 2>/dev/null; do
              sleep 2
            done

            # Create databases
            echo "Creating databases..."
            curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app

            # Create default admin user
            echo "Creating default admin user..."
            curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/_users/org.couchdb.user:$COUCHDB_USER \
              -H "Content-Type: application/json" \
              -d "{
                \"name\": \"$COUCHDB_USER\",
                \"password\": \"$COUCHDB_PASSWORD\",
                \"roles\": [\"admin\"],
                \"type\": \"user\"
              }"

            # Create design documents for views
            echo "Creating design documents..."
            curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/medications \
              -H "Content-Type: application/json" \
              -d '{
                "views": {
                  "by_name": {
                    "map": "function(doc) { if (doc.type === \"medication\") emit(doc.name, doc); }"
                  },
                  "by_user": {
                    "map": "function(doc) { if (doc.type === \"medication\") emit(doc.userId, doc); }"
                  }
                }
              }'

            curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/reminders \
              -H "Content-Type: application/json" \
              -d '{
                "views": {
                  "by_medication": {
                    "map": "function(doc) { if (doc.type === \"reminder\") emit(doc.medicationId, doc); }"
                  },
                  "by_user": {
                    "map": "function(doc) { if (doc.type === \"reminder\") emit(doc.userId, doc); }"
                  }
                }
              }'

            # Create a sample user document for reference
              # Create design document for authentication users
              curl -X PUT http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app/_design/auth \
                -H "Content-Type: application/json" \
                -d '{
                  "views": {
                    "by_username": {
                      "map": "function(doc) { if (doc.type === \"user\" && doc.username) emit(doc.username, doc); }"
                    },
                    "by_email": {
                      "map": "function(doc) { if (doc.type === \"user\" && doc.email) emit(doc.email, doc); }"
                    }
                  }
                }'
            echo "Creating sample user document..."
            curl -X POST http://$COUCHDB_USER:$COUCHDB_PASSWORD@couchdb-service:5984/meds_app \
              -H "Content-Type: application/json" \
              -d '{
                "type": "user",
                "name": "sample_user",
                "email": "user@example.com",
                "createdAt": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
              }'

            echo "Database seeding completed with default admin user"
          command:
          - /bin/sh
          - -c
          env:
          - name: COUCHDB_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: couchdb-secret-7ck2cc96g5
          - name: COUCHDB_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: couchdb-secret-7ck2cc96g5
          image: couchdb:3.3.2
          name: db-seeder
        restartPolicy: Never
- apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-ingress","namespace":"rxminder-dev"},"spec":{"ingressClassName":"nginx","rules":[{"host":"rxminder-dev.local","http":{"paths":[{"backend":{"service":{"name":"rxminder-frontend-service","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}]}}
    labels:
      app: rxminder
      component: frontend
      environment: dev
      version: v1.0.0
    name: rxminder-ingress
    namespace: rxminder-dev
  spec:
    ingressClassName: nginx
    rules:
    - host: rxminder-dev.local
      http:
        paths:
        - backend:
            service:
              name: rxminder-frontend-service
              port:
                number: 80
          path: /
          pathType: Prefix
- apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"networking.k8s.io/v1","kind":"NetworkPolicy","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"database","environment":"dev","version":"v1.0.0"},"name":"rxminder-database-policy","namespace":"rxminder-dev"},"spec":{"egress":[{"ports":[{"port":5984,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"database"}}}]}],"ingress":[{"from":[{"podSelector":{"matchLabels":{"component":"frontend"}}}],"ports":[{"port":5984,"protocol":"TCP"}]}],"podSelector":{"matchLabels":{"component":"database"}},"policyTypes":["Ingress","Egress"]}}
    labels:
      app: rxminder
      component: database
      environment: dev
      version: v1.0.0
    name: rxminder-database-policy
    namespace: rxminder-dev
  spec:
    egress:
    - ports:
      - port: 5984
        protocol: TCP
      to:
      - podSelector:
          matchLabels:
            component: database
    ingress:
    - from:
      - podSelector:
          matchLabels:
            component: frontend
      ports:
      - port: 5984
        protocol: TCP
    podSelector:
      matchLabels:
        component: database
    policyTypes:
    - Ingress
    - Egress
- apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"networking.k8s.io/v1","kind":"NetworkPolicy","metadata":{"annotations":{},"labels":{"app":"rxminder","component":"frontend","environment":"dev","version":"v1.0.0"},"name":"rxminder-frontend-policy","namespace":"rxminder-dev"},"spec":{"egress":[{"ports":[{"port":5984,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"database"}}}]},{"ports":[{"port":80,"protocol":"TCP"}],"to":[{"podSelector":{"matchLabels":{"component":"frontend"}}}]}],"ingress":[{"from":[{"podSelector":{"matchLabels":{"component":"frontend"}}}],"ports":[{"port":80,"protocol":"TCP"}]}],"podSelector":{"matchLabels":{"component":"frontend"}},"policyTypes":["Ingress","Egress"]}}
    labels:
      app: rxminder
      component: frontend
      environment: dev
      version: v1.0.0
    name: rxminder-frontend-policy
    namespace: rxminder-dev
  spec:
    egress:
    - ports:
      - port: 5984
        protocol: TCP
      to:
      - podSelector:
          matchLabels:
            component: database
    - ports:
      - port: 80
        protocol: TCP
      to:
      - podSelector:
          matchLabels:
            component: frontend
    ingress:
    - from:
      - podSelector:
          matchLabels:
            component: frontend
      ports:
      - port: 80
        protocol: TCP
    podSelector:
      matchLabels:
        component: frontend
    policyTypes:
    - Ingress
    - Egress
kind: List
metadata: {}

## Migration Path
- Legacy shell scripts remain available for backward compatibility
- Gradual migration: dev → staging → production
- Zero-downtime deployment capability

Co-authored-by: Assistant <assistant@anthropic.com>
2025-09-07 20:47:10 -07:00

💊 RxMinder

A modern, secure web application for managing medication schedules and reminders. Built with React, TypeScript, CouchDB, and Docker for reliable medication tracking and adherence monitoring.

License TypeScript React Docker

Features

🔐 Authentication & Security

  • Email/Password Authentication with secure password hashing (bcrypt)
  • OAuth Integration (Google, GitHub) for social login
  • Email Verification for account activation
  • Password Reset functionality with secure tokens
  • Admin Interface for user management
  • Role-based Access Control (User, Admin)

💊 Medication Management

  • Add/Edit/Delete Medications with dosage and frequency
  • Flexible Scheduling (Daily, Twice/Three times daily, Custom intervals)
  • Visual Medication Cards with custom icons
  • Medication History tracking

Reminder System

  • Smart Scheduling based on medication frequency
  • Dose Tracking (Taken, Missed, Upcoming)
  • Custom Reminders with personalized messages
  • Adherence Statistics and progress monitoring

📊 Analytics & Insights

  • Daily Adherence Statistics with visual charts
  • Medication-specific Analytics (taken vs missed doses)
  • Progress Tracking over time
  • Export Capabilities for healthcare providers

🎨 User Experience

  • Responsive Design for mobile and desktop
  • Dark/Light Theme support
  • Intuitive Interface with modern design
  • Onboarding Flow for new users
  • Avatar Customization with image upload

🏗️ Architecture

Frontend Stack

  • React 19 with TypeScript
  • Vite for fast development and building
  • Modern CSS with responsive design
  • Component-based Architecture

Backend Services

  • CouchDB for document-based data storage
  • Mailgun for email delivery (verification, password reset)
  • bcrypt for secure password hashing
  • JWT-like token system for authentication

Infrastructure

  • Docker & Docker Compose for containerization
  • Nginx for production static file serving
  • Multi-stage Builds for optimized images
  • Health Checks for service monitoring

Development Tools

  • TypeScript for type safety and modern JavaScript features
  • ESLint for code quality and consistent style
  • Prettier for automated code formatting
  • Pre-commit hooks for automated quality checks
  • Bun for fast package management and development
  • Environment-based Configuration for flexible deployments

🚀 Quick Start

Prerequisites

1. Clone and Setup

git clone <repository-url>
cd meds
./setup.sh

# Validate configuration (optional)
./validate-env.sh

2. Configure Environment

# Copy the template and customize
cp .env.example .env

# Edit .env with your credentials
nano .env

3. Deploy

# Quick deployment
./deploy.sh

# Or manual Docker Compose
docker compose up -d

4. Access the Application

🔧 Development

Local Development

# Install dependencies
bun install

# Start development server
bun run dev

# Run with real CouchDB (Docker)
docker compose up -d couchdb
VITE_COUCHDB_URL=http://localhost:5984 bun run dev

Makefile Commands

For convenience, you can use the included Makefile which wraps all package.json scripts with organized categories:

# Show all available commands
make help

# Quick development commands
make start          # Start development server
make build          # Build the application
make test           # Run unit tests

# Testing commands
make test-all       # Run all tests (unit + integration + e2e)
make test-e2e       # Run end-to-end tests
make test-coverage  # Run tests with coverage

# Code quality
make lint           # Run all linting checks
make pre-commit     # Run pre-commit checks
make full-check     # Run complete code quality check

# Kubernetes deployment
make k8s-deploy     # Deploy to Kubernetes
make k8s-undeploy   # Remove from Kubernetes
make k8s-deploy-dry # Dry run deployment

# Docker operations
make docker-build   # Build Docker images
make docker-setup   # Setup Docker buildx

# Quick combinations
make build-and-test # Build and test
make quick-deploy   # Build and deploy to K8s
make reset          # Clean and reinstall

All Makefile targets are organized into logical categories (Development, Testing, Code Quality, Kubernetes, Docker, etc.) and provide colored output for better visibility.

Code Quality

This project includes comprehensive code quality tools and pre-commit hooks. See docs/development/CODE_QUALITY.md for detailed documentation.

# Format code
bun run format

# Check formatting
bun run format:check

# Lint code
bun run lint

# Fix lint issues
bun run lint:fix

# Type checking
bun run type-check

# Run pre-commit checks
bun run pre-commit

# Setup pre-commit hooks (one-time)
./scripts/setup-pre-commit.sh

Automatic Quality Checks: Pre-commit hooks automatically format code, run linting, type checking, and security scans on every commit.

Testing

# Run tests
bun run test

# Run specific test file
bun run test auth.integration.test.ts

🔐 Security & Configuration

Environment Variables

Required Variables

# CouchDB Configuration
COUCHDB_USER=admin
COUCHDB_PASSWORD=your-secure-password
VITE_COUCHDB_URL=http://localhost:5984
VITE_COUCHDB_USER=admin
VITE_COUCHDB_PASSWORD=your-secure-password

Optional Variables

# Mailgun (for email features)
MAILGUN_API_KEY=your-mailgun-api-key
MAILGUN_DOMAIN=your-domain.com
MAILGUN_FROM_EMAIL=noreply@your-domain.com

# Production Settings
NODE_ENV=production

Security Best Practices

  1. 🔒 Never commit .env files - Already in .gitignore
  2. 🛡️ Use strong passwords - Minimum 8 characters with mixed case, numbers, symbols
  3. 🔄 Rotate credentials regularly - Especially in production
  4. 📧 Verify email configuration - Test Mailgun setup before production
  5. 🔍 Monitor logs - Check Docker logs for security events
  6. 🚪 Limit access - Use firewall rules for production deployments

Credential Management Methods

Development

# Method 1: .env file (recommended for local dev)
cp .env.example .env
# Edit with your values

# Method 2: Shell environment
export COUCHDB_PASSWORD="secure-password"
export MAILGUN_API_KEY="key-123..."

Production

# Method 1: Secure deployment script
./deploy.sh production

# Method 2: CI/CD with environment variables
# Set in GitHub Actions, GitLab CI, etc.

# Method 3: External secrets management
# AWS Secrets Manager, Azure Key Vault, etc.

Docker Deployment

# Using .env file
docker compose --env-file .env.production up -d

# Using environment variables
COUCHDB_PASSWORD="secure-password" docker compose up -d

📁 Project Structure

meds/
├── 📄 README.md                    # This documentation
├──  package.json                 # Dependencies and scripts
├── ⚙️ vite.config.ts               # Build configuration
├── 📝 tsconfig.json                # TypeScript configuration
├── 🎨 index.html                   # Entry point
├── 🚀 deploy.sh                    # Secure deployment script
├── 🔧 setup.sh                     # Development setup script
├── 🌱 seed-production.js           # Database seeding
├── 🧪 test-production.js           # Production testing
├── 🔒 .env.example                 # Environment template
│
├── 📁 docker/                      # Container configuration
│   ├── 🐳 Dockerfile              # Multi-stage Docker build
│   ├── 🐳 docker-compose.yaml     # Service orchestration
│   ├── 🌐 nginx.conf              # Production web server config
│   └── 🚫 .dockerignore           # Docker ignore patterns
│
├── 📁 components/                  # React components
│   ├── 🔐 AuthPage.tsx            # Login/register interface
│   ├── 👑 AdminInterface.tsx      # Admin user management
│   ├── 💊 AddMedicationModal.tsx  # Medication creation
│   ├── ⏰ ReminderCard.tsx        # Reminder display
│   ├── 📊 StatsModal.tsx          # Analytics dashboard
│   └── ...                        # Other UI components
│
├── 📁 services/                    # Business logic & APIs
│   ├── 🗄️ couchdb.ts              # Mock database service
│   ├── 🗄️ couchdb.production.ts   # Real CouchDB service
│   ├── 🏭 couchdb.factory.ts      # Service factory
│   ├── 📧 mailgun.service.ts      # Email delivery
│   ├── 📧 mailgun.config.ts       # Email configuration
│   ├── 🌱 database.seeder.ts      # Data seeding
│   └── 📁 auth/                   # Authentication services
│       ├── 🔐 auth.service.ts     # Core auth logic
│       ├── ✉️ emailVerification.service.ts
│       └── 📁 __tests__/          # Test suites
│
├── 📁 contexts/                    # React context providers
│   └── 👤 UserContext.tsx         # User state management
│
├── 📁 hooks/                       # Custom React hooks
│   ├── 💾 useLocalStorage.ts      # Persistent storage
│   ├── ⚙️ useSettings.ts          # User preferences
│   └── 🎨 useTheme.ts             # Theme management
│
└── 📁 utils/                       # Utility functions
    └── ⏰ schedule.ts              # Reminder scheduling

🎯 API Reference

Authentication Endpoints

Register User

authService.register(email: string, password: string, username?: string)
// Returns: { user: User, verificationToken: EmailVerificationToken }

Login User

authService.login({ email: string, password: string });
// Returns: { user: User, accessToken: string, refreshToken: string }

OAuth Login

authService.loginWithOAuth(provider: 'google' | 'github', userData: OAuthUserData)
// Returns: { user: User, accessToken: string, refreshToken: string }

Change Password

authService.changePassword(userId: string, currentPassword: string, newPassword: string)
// Returns: { success: boolean, message: string }

Database Operations

User Management

dbService.saveUser(user: User): Promise<User>
dbService.findUserByEmail(email: string): Promise<User | null>
dbService.updateUser(userId: string, updates: Partial<User>): Promise<User>
dbService.deleteUser(userId: string): Promise<void>

Medication Management

dbService.saveMedication(medication: Medication): Promise<Medication>
dbService.getMedications(userId: string): Promise<Medication[]>
dbService.updateMedication(medicationId: string, updates: Partial<Medication>): Promise<Medication>
dbService.deleteMedication(medicationId: string): Promise<void>

Reminder & Dose Tracking

dbService.saveReminder(reminder: CustomReminder): Promise<CustomReminder>
dbService.getReminders(userId: string): Promise<CustomReminder[]>
dbService.saveTakenDose(dose: TakenDose): Promise<void>
dbService.getTakenDoses(userId: string, date?: string): Promise<TakenDoses>

🐳 Docker Reference

Build Images

# Build all services
docker compose build

# Build specific service
docker compose build frontend

# Build with no cache
docker compose build --no-cache

Manage Services

# Start all services
docker compose up -d

# Start specific service
docker compose up -d couchdb

# Stop all services
docker compose down

# View logs
docker compose logs
docker compose logs frontend

Database Management

# Access CouchDB container
docker compose exec couchdb bash

# Backup database
docker compose exec couchdb curl -X GET http://admin:password@localhost:5984/users/_all_docs?include_docs=true

# Restore database
# Use CouchDB Fauxton interface or curl commands

🧪 Testing & Quality Assurance

Development Testing

# Run all unit tests
bun run test

# Run tests in watch mode
bun run test:watch

# Run with coverage
bun run test:coverage

# Run integration tests
bun run test:integration

# Run E2E tests with Playwright
bun run test:e2e

# Run E2E tests in UI mode
bun run test:e2e:ui

# Debug E2E tests
bun run test:e2e:debug

# Run all tests (unit + integration + e2e)
bun run test:all

Testing Structure

  • Unit Tests: Jest-based tests for individual functions and components
  • Integration Tests: Production environment validation and service testing
  • E2E Tests: Playwright-based full user journey testing across browsers
  • Manual Tests: Browser console debugging scripts

See tests/README.md for detailed testing documentation.

Test Production Environment

# Run comprehensive production tests
bun test-production.js

# Manual testing checklist
./deploy.sh                    # Deploy environment
# Visit http://localhost:8080
# Test user registration/login
# Test admin interface
# Test medication management
# Test password change
# Verify data persistence

Performance Testing

# Check service health
docker compose ps
curl -f http://localhost:5984/_up
curl -f http://localhost:8080

# Monitor resource usage
docker stats

Security Testing

# Check for vulnerable dependencies
bun audit

# Validate environment configuration
./deploy.sh --dry-run

# Test authentication flows
# - Registration with weak passwords
# - Login with wrong credentials
# - Access admin without proper role

🚀 Deployment Guide

Development Deployment

# Quick local setup
./setup.sh

Production Deployment

# Secure production deployment
./deploy.sh production

Cloud Deployment

AWS EC2

# 1. Launch EC2 instance with Docker
# 2. Clone repository
git clone <repo-url>
cd meds

# 3. Configure environment
cp .env.example .env
# Edit .env with production values

# 4. Deploy
./deploy.sh production

Google Cloud Run

# Build and push image
gcloud builds submit --tag gcr.io/PROJECT-ID/meds-app

# Deploy with environment variables
gcloud run deploy meds-app \
  --image gcr.io/PROJECT-ID/meds-app \
  --set-env-vars COUCHDB_URL=your-couchdb-url \
  --set-env-vars MAILGUN_API_KEY=your-key

Kubernetes (Template-Based)

# 1. Copy and configure environment
cp .env.example .env
# Edit .env with your secure credentials

# 2. Deploy with templates (recommended)
./scripts/k8s-deploy-template.sh deploy

# Alternative: Manual deployment
# Create secrets manually
kubectl create secret generic meds-secrets \
  --from-literal=couchdb-password=secure-password \
  --from-literal=mailgun-api-key=your-key

# Apply manifests
kubectl apply -f k8s/

🔍 Troubleshooting

Common Issues

Environment Variables Not Loading

# Check .env file exists and is properly formatted
cat .env

# Verify Docker Compose uses env file
docker compose config

CouchDB Connection Issues

# Check CouchDB health
curl -u admin:password http://localhost:5984/_up

# Verify credentials
docker compose logs couchdb

# Reset database
docker compose down
docker volume rm meds_couchdb-data
docker compose up -d

Frontend Build Failures

# Clear node modules and reinstall
rm -rf node_modules bun.lockb
bun install

# Check for TypeScript errors
bun run type-check

# Build with verbose output
bun run build --verbose

Email Not Sending

# Verify Mailgun configuration
echo $MAILGUN_API_KEY
echo $MAILGUN_DOMAIN

# Check Mailgun service logs
docker compose logs frontend | grep -i mailgun

# Test Mailgun API directly
curl -s --user 'api:YOUR_API_KEY' \
  https://api.mailgun.net/v3/YOUR_DOMAIN/messages \
  -F from='test@YOUR_DOMAIN' \
  -F to='you@example.com' \
  -F subject='Test' \
  -F text='Testing'

Performance Issues

# Check resource usage
docker stats

# Optimize Docker images
docker system prune -a

# Monitor application performance
docker compose logs --tail=100 frontend

Debug Mode

# Run with debug logging
DEBUG=* docker compose up

# Access container for debugging
docker compose exec frontend sh
docker compose exec couchdb bash

📚 Documentation

Complete Documentation Index

For comprehensive documentation, visit docs/README.md which includes:

🏗️ Architecture & Design

🚀 Setup & Configuration

💻 Development

🚢 Deployment

🔄 Migration Guides

📚 Additional Resources

Documentation

Development Tools

Security Resources

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit changes: git commit -m 'Add amazing feature'
  4. Push to branch: git push origin feature/amazing-feature
  5. Open a Pull Request

Development Workflow

# Setup development environment
./setup.sh

# Make changes and test
bun run dev
bun run lint
bun run type-check

# Test in production environment
./deploy.sh
bun test-production.js

# Submit pull request

📚 Documentation

Project Documentation

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • CouchDB Team for the robust database system
  • Mailgun for reliable email delivery
  • React Team for the excellent frontend framework
  • Docker Team for containerization technology
  • Bun Team for the fast JavaScript runtime

Built with ❤️ for better medication adherence and health outcomes.

For support, please open an issue on GitHub or contact the development team.<2E> Documentation

Description
No description provided
Readme 792 KiB
Languages
TypeScript 95.9%
JavaScript 2.5%
Makefile 0.9%
Dockerfile 0.4%
HTML 0.2%
Other 0.1%