diff --git a/.gitea/docker-compose.ci.yml b/.gitea/docker-compose.ci.yml deleted file mode 100644 index 34caa9a..0000000 --- a/.gitea/docker-compose.ci.yml +++ /dev/null @@ -1,45 +0,0 @@ -# Gitea Actions CI/CD Docker Compose Override -# This file provides CI-specific configurations for Gitea Actions - -version: '3.8' - -services: - # Frontend service with CI optimizations - frontend: - build: - context: . - target: builder - cache_from: - - ${REGISTRY:-gitea.example.com}/${IMAGE_NAME:-rxminder}:buildcache - args: - # Use build args from CI environment - - VITE_COUCHDB_URL=${VITE_COUCHDB_URL:-http://couchdb:5984} - - VITE_COUCHDB_USER=${VITE_COUCHDB_USER:-admin} - - VITE_COUCHDB_PASSWORD=${VITE_COUCHDB_PASSWORD:-change-this-secure-password} - - APP_BASE_URL=${APP_BASE_URL:-http://localhost:8080} - - VITE_GOOGLE_CLIENT_ID=${VITE_GOOGLE_CLIENT_ID:-} - - VITE_GITHUB_CLIENT_ID=${VITE_GITHUB_CLIENT_ID:-} - - NODE_ENV=production - environment: - - CI=true - labels: - - 'gitea.ci=true' - - 'gitea.project=rxminder' - - # Test database for CI - couchdb-test: - image: couchdb:3.3.2 - environment: - - COUCHDB_USER=admin - - COUCHDB_PASSWORD=test-secure-password - ports: - - '5985:5984' - volumes: - - couchdb_test_data:/opt/couchdb/data - labels: - - 'gitea.ci=true' - - 'gitea.service=test-database' - -volumes: - couchdb_test_data: - driver: local diff --git a/.gitea/gitea-bake.hcl b/.gitea/gitea-bake.hcl deleted file mode 100644 index 2e4074a..0000000 --- a/.gitea/gitea-bake.hcl +++ /dev/null @@ -1,156 +0,0 @@ -# Gitea-specific Docker Bake file for advanced multi-platform builds -# Usage: docker buildx bake -f gitea-bake.hcl - -variable "GITEA_REGISTRY" { - default = notequal("", GITEA_REGISTRY) ? GITEA_REGISTRY : "ghcr.io" -} - -variable "GITEA_REPOSITORY" { - default = notequal("", GITEA_REPOSITORY) ? GITEA_REPOSITORY : "user/rxminder" -} - -variable "TAG" { - default = "latest" -} - -variable "GITEA_SHA" { - default = "dev" -} - -variable "VITE_COUCHDB_URL" { - default = "http://localhost:5984" -} - -variable "VITE_COUCHDB_USER" { - default = "admin" -} - -variable "VITE_COUCHDB_PASSWORD" { - default = "change-this-secure-password" -} - -variable "APP_BASE_URL" { - default = "http://localhost:8080" -} - -variable "VITE_GOOGLE_CLIENT_ID" { - default = "" -} - -variable "VITE_GITHUB_CLIENT_ID" { - default = "" -} - -group "default" { - targets = ["app"] -} - -group "ci" { - targets = ["app-ci"] -} - -target "app" { - dockerfile = "Dockerfile" - context = "." - platforms = [ - "linux/amd64", - "linux/arm64" - ] - - tags = [ - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:${TAG}", - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:latest" - ] - - args = { - VITE_COUCHDB_URL = "${VITE_COUCHDB_URL}" - VITE_COUCHDB_USER = "${VITE_COUCHDB_USER}" - VITE_COUCHDB_PASSWORD = "${VITE_COUCHDB_PASSWORD}" - APP_BASE_URL = "${APP_BASE_URL}" - VITE_GOOGLE_CLIENT_ID = "${VITE_GOOGLE_CLIENT_ID}" - VITE_GITHUB_CLIENT_ID = "${VITE_GITHUB_CLIENT_ID}" - NODE_ENV = "production" - } - - # Gitea registry caching - cache-from = [ - "type=registry,ref=${GITEA_REGISTRY}/${GITEA_REPOSITORY}:buildcache" - ] - - cache-to = [ - "type=registry,ref=${GITEA_REGISTRY}/${GITEA_REPOSITORY}:buildcache,mode=max" - ] -} - -# CI-specific target with commit SHA tagging -target "app-ci" { - inherits = ["app"] - tags = [ - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:${GITEA_SHA}", - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:latest" - ] - - # Enhanced CI-specific features - attest = [ - "type=provenance,mode=max", - "type=sbom" - ] - - # CI registry push - output = ["type=registry"] -} - -# Development target for local builds -target "dev" { - inherits = ["app"] - platforms = ["linux/amd64"] - tags = ["rxminder:dev"] - - # Local caching only - cache-from = ["type=registry,ref=${GITEA_REGISTRY}/${GITEA_REPOSITORY}:buildcache"] - cache-to = ["type=registry,ref=${GITEA_REGISTRY}/${GITEA_REPOSITORY}:buildcache"] - - # Load locally instead of push - output = ["type=docker"] -} - -# Production target with full attestations -target "prod" { - inherits = ["app-ci"] - - # Production-specific tags - tags = [ - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:prod-${TAG}", - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:production" - ] - - # Full security attestations for production - attest = [ - "type=provenance,mode=max", - "type=sbom" - ] -} - -# Staging target -target "staging" { - inherits = ["app"] - platforms = ["linux/amd64"] # Single platform for staging - - tags = [ - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:staging-${TAG}", - "${GITEA_REGISTRY}/${GITEA_REPOSITORY}:staging" - ] - - # Staging-specific build args - args = { - VITE_COUCHDB_URL = "${VITE_COUCHDB_URL}" - VITE_COUCHDB_USER = "${VITE_COUCHDB_USER}" - VITE_COUCHDB_PASSWORD = "${VITE_COUCHDB_PASSWORD}" - APP_BASE_URL = "http://staging.localhost:8080" - VITE_GOOGLE_CLIENT_ID = "${VITE_GOOGLE_CLIENT_ID}" - VITE_GITHUB_CLIENT_ID = "${VITE_GITHUB_CLIENT_ID}" - NODE_ENV = "staging" - } - - output = ["type=registry"] -} diff --git a/KUSTOMIZE_MIGRATION.md b/KUSTOMIZE_MIGRATION.md deleted file mode 100644 index 41ff784..0000000 --- a/KUSTOMIZE_MIGRATION.md +++ /dev/null @@ -1,299 +0,0 @@ -# Kustomize Migration Complete! πŸŽ‰ - -## Migration Summary - -The rxminder application has been successfully migrated from shell script-based Kubernetes deployments to **Kustomize**, providing a more maintainable, scalable, and GitOps-ready deployment strategy. - -## What Was Accomplished - -### βœ… 1. Complete Base Resources - -- βœ… Converted all template files to base Kustomize resources -- βœ… Created `frontend-deployment.yaml`, `frontend-service.yaml` -- βœ… Created `couchdb-statefulset.yaml`, `couchdb-service.yaml`, `couchdb-pvc.yaml` -- βœ… Created `ingress.yaml`, `network-policy.yaml`, `hpa.yaml`, `db-seed-job.yaml` -- βœ… Removed template variables and used Kustomize's generators -- βœ… Set up ConfigMap generation from `config.env` -- βœ… Configured Secret generation for CouchDB credentials - -### βœ… 2. Environment Overlays - -- βœ… **Development Overlay**: Optimized for local development - - Namespace: `rxminder-dev` - - Minimal resources (16Mi-32Mi memory) - - Debug logging enabled - - Weak passwords for development - - Single replica for resource conservation -- βœ… **Production Overlay**: Enterprise-ready configuration - - Namespace: `rxminder-prod` - - Production resources (256Mi-512Mi memory) - - Security hardening (security contexts, network policies) - - TLS/HTTPS enabled - - High availability (3 frontend replicas) - - Production storage (10Gi SSD) - - Monitoring and alerting enabled - -### βœ… 3. Updated Makefile - -- βœ… Added comprehensive Kustomize targets -- βœ… Maintained backward compatibility with legacy shell scripts -- βœ… Created convenient aliases for quick deployment -- βœ… Added validation and debugging commands - -### βœ… 4. Documentation - -- βœ… Comprehensive `k8s-kustomize/README.md` -- βœ… Migration guide and troubleshooting -- βœ… Best practices and security considerations - -## Key Benefits Achieved - -### πŸš€ Simplified Deployment - -```bash -# Before (shell scripts with variable substitution) -APP_NAME=rxminder NAMESPACE=production ./scripts/deploy.sh - -# After (Kustomize) -make deploy-prod -# or -kubectl apply -k k8s-kustomize/overlays/prod -``` - -### πŸ”’ Environment Isolation - -- **Clear separation** between dev, staging, and production -- **Namespace isolation** prevents cross-environment contamination -- **Environment-specific configurations** without duplication - -### πŸ”§ GitOps Ready - -- **ArgoCD/Flux compatible** out of the box -- **Declarative configuration** with no templating complexity -- **Git-based workflow** for deployment automation - -### βœ… Better Validation - -- **Built-in YAML validation** catches errors early -- **Dry-run capabilities** for safe deployments -- **Configuration drift detection** - -### πŸ“ˆ Standard Approach - -- **Kubernetes-native** solution (no external dependencies) -- **Industry standard** approach -- **Better team onboarding** with familiar tooling - -## Available Commands - -### Quick Start Commands - -```bash -# Development -make deploy-dev # Deploy to development -make quick-deploy-dev # Build and deploy to development -make status-dev # Check development status - -# Production -make deploy-prod # Deploy to production -make quick-deploy-prod # Build and deploy to production -make status-prod # Check production status -``` - -### Validation & Debugging - -```bash -make validate-kustomize # Validate all configurations -make kustomize-dry-run-dev # Test development deployment -make kustomize-diff-prod # Show production differences -make kustomize-build-dev # Build development manifests -``` - -### Legacy Support (Still Available) - -```bash -make deploy-dev # Deploy to development environment -make deploy-prod # Deploy to production environment -make undeploy-dev # Remove development deployment -make undeploy-prod # Remove production deployment -``` - -## Directory Structure - -``` -k8s-kustomize/ -β”œβ”€β”€ base/ # Shared base configuration -β”‚ β”œβ”€β”€ kustomization.yaml # Base kustomization -β”‚ β”œβ”€β”€ config.env # Environment variables -β”‚ β”œβ”€β”€ frontend-deployment.yaml # Frontend workload -β”‚ β”œβ”€β”€ couchdb-statefulset.yaml # Database workload -β”‚ β”œβ”€β”€ *-service.yaml # Services -β”‚ β”œβ”€β”€ ingress.yaml # Ingress configuration -β”‚ β”œβ”€β”€ network-policy.yaml # Security policies -β”‚ └── ... # Other resources -β”œβ”€β”€ overlays/ -β”‚ β”œβ”€β”€ dev/ # Development environment -β”‚ β”‚ └── kustomization.yaml # Dev customizations -β”‚ └── prod/ # Production environment -β”‚ β”œβ”€β”€ kustomization.yaml # Prod customizations -β”‚ β”œβ”€β”€ frontend-resources.yaml # Prod frontend config -β”‚ β”œβ”€β”€ couchdb-resources.yaml # Prod database config -β”‚ └── ingress-prod.yaml # Prod ingress config -└── README.md # Comprehensive documentation -``` - -## Security Enhancements - -### Production Security Features - -- βœ… **Security contexts**: Non-root containers, read-only filesystems -- βœ… **Network policies**: Restricted pod-to-pod communication -- βœ… **Resource limits**: Prevent resource exhaustion attacks -- βœ… **TLS encryption**: HTTPS with cert-manager integration -- βœ… **RBAC ready**: Role-based access control compatible -- βœ… **Secret management**: External secret store integration points - -### Development Security - -- βœ… **Namespace isolation**: Separated from production -- βœ… **Weak credentials**: Safe for development environment -- βœ… **Relaxed policies**: Optimized for developer productivity - -## Next Steps & Recommendations - -### Immediate Actions (Week 1) - -1. **Test deployments** in development environment -2. **Validate configuration** with your team -3. **Update CI/CD pipelines** to use Kustomize commands -4. **Train team members** on new deployment process - -### Short-term Goals (Month 1) - -1. **Production deployment**: Schedule maintenance window -2. **Secret management**: Implement external secret store - - Consider: External Secrets Operator, HashiCorp Vault, or cloud-native solutions -3. **Monitoring integration**: Connect with your monitoring stack -4. **Documentation updates**: Update runbooks and procedures - -### Long-term Goals (Quarter 1) - -1. **GitOps implementation**: Set up ArgoCD or Flux -2. **Multi-environment**: Add staging environment overlay -3. **Advanced features**: Implement blue-green or canary deployments -4. **Cleanup**: Remove legacy shell scripts after validation - -### Adding New Environments - -To add a staging environment: - -```bash -# 1. Create staging overlay -mkdir -p k8s-kustomize/overlays/staging - -# 2. Create staging kustomization.yaml -cat > k8s-kustomize/overlays/staging/kustomization.yaml << EOF -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - ../../base - -namespace: rxminder-staging - -labels: - - pairs: - environment: staging - -images: - - name: gitea-http.taildb3494.ts.net/will/rxminder - newTag: staging -EOF - -# 3. Add Makefile targets -# Add to Makefile: -# deploy-staging: kustomize-deploy-staging -# kustomize-deploy-staging: -# kubectl apply -k k8s-kustomize/overlays/staging -``` - -## Migration Path Options - -### Option 1: Gradual Migration (Recommended) - -1. βœ… **Development first**: Already completed and tested -2. **Staging next**: Validate with staging workloads -3. **Production last**: Schedule maintenance window for production migration - -### Option 2: Parallel Running - -1. **Keep both systems**: Run legacy and Kustomize side-by-side -2. **Test extensively**: Validate Kustomize deployments -3. **Switch over**: Move traffic from legacy to Kustomize deployments - -### Option 3: Feature Flag Approach - -1. **Environment variable**: Control deployment method via feature flag -2. **Gradual rollout**: Enable Kustomize for percentage of deployments -3. **Full migration**: Switch to 100% Kustomize when validated - -## Rollback Plan - -If issues arise, legacy deployment is still available: - -```bash -# Emergency rollback procedures -make undeploy-dev # Remove development deployment -make undeploy-prod # Remove production deployment -# Then redeploy using current configurations -``` - -## Validation Checklist - -Before production migration: - -- [ ] Development deployment working correctly -- [ ] All services accessible and functional -- [ ] Database persistence working -- [ ] Ingress and networking functional -- [ ] Resource limits appropriate -- [ ] Security policies working -- [ ] Monitoring and logging operational -- [ ] Team trained on new commands -- [ ] CI/CD updated -- [ ] Rollback plan tested - -## Success Metrics - -Track these metrics to validate the migration success: - -- **Deployment time**: Should be faster and more reliable -- **Error rate**: Fewer deployment failures -- **Time to recovery**: Faster rollbacks and fixes -- **Team productivity**: Easier environment management -- **Configuration drift**: Better consistency across environments - -## Support & Resources - -- **Documentation**: `k8s-kustomize/README.md` -- **Troubleshooting**: Check events and logs with provided commands -- **Validation**: Use `make validate-kustomize` for configuration checks -- **Testing**: Use dry-run commands before actual deployments - -## Conclusion - -The Kustomize migration provides a robust foundation for scaling your Kubernetes deployments. The new system offers: - -- **Maintainability**: Clear separation of concerns -- **Scalability**: Easy to add new environments -- **Security**: Production-grade security configurations -- **Reliability**: Better validation and error handling -- **Team Efficiency**: Standardized, well-documented processes - -The legacy shell script approach is still available as a fallback, ensuring zero downtime during the migration period. Take your time to validate the new system thoroughly before fully committing to the migration. - -**Happy deploying! πŸš€** - ---- - -_This migration was completed on $(date). For questions or issues, refer to the troubleshooting section in the README or consult your team's Kubernetes documentation._ diff --git a/docker-bake.hcl b/docker-bake.hcl deleted file mode 100644 index 970e231..0000000 --- a/docker-bake.hcl +++ /dev/null @@ -1,213 +0,0 @@ -# Docker Bake configuration for RxMinder -# Provides advanced multi-platform build configuration - -variable "DOCKER_REGISTRY" { - default = "" -} - -variable "DOCKER_TAG" { - default = "latest" -} - -variable "APP_NAME" { - default = "RxMinder" -} - -variable "NODE_ENV" { - default = "production" -} - -# Get git information for tagging -function "git_hash" { - params = [] - result = notequal("", GIT_COMMIT) ? substr(GIT_COMMIT, 0, 7) : "unknown" -} - -function "git_branch" { - params = [] - result = notequal("", GIT_BRANCH) ? replace(GIT_BRANCH, "/", "-") : "unknown" -} - -# Main target group -group "default" { - targets = ["app"] -} - -# Production target group -group "production" { - targets = ["app-prod"] -} - -# Development target group -group "development" { - targets = ["app-dev"] -} - -# All targets group -group "all" { - targets = ["app", "app-dev", "app-prod"] -} - -# Base application target -target "app" { - dockerfile = "docker/Dockerfile" - contexts = { - src = "." - } - - platforms = [ - "linux/amd64", - "linux/arm64" - ] - - args = { - APP_NAME = APP_NAME - NODE_ENV = NODE_ENV - VITE_COUCHDB_URL = "http://couchdb:5984" - VITE_COUCHDB_USER = "admin" - VITE_COUCHDB_PASSWORD = "change-this-secure-password" - APP_BASE_URL = "http://localhost:8080" - VITE_GOOGLE_CLIENT_ID = "" - VITE_GITHUB_CLIENT_ID = "" - MAILGUN_API_KEY = "" - MAILGUN_DOMAIN = "" - MAILGUN_FROM_EMAIL = "" - } - - tags = [ - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:${DOCKER_TAG}" : "rxminder:${DOCKER_TAG}", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:latest" : "rxminder:latest", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:${git_hash()}" : "rxminder:${git_hash()}" - ] - - labels = { - "org.opencontainers.image.title" = "RxMinder" - "org.opencontainers.image.description" = "Medication reminder application" - "org.opencontainers.image.version" = DOCKER_TAG - "org.opencontainers.image.revision" = git_hash() - "org.opencontainers.image.source" = "https://github.com/username/rxminder" - "org.opencontainers.image.created" = timestamp() - "org.opencontainers.image.licenses" = "MIT" - } - - cache-from = [ - "type=gha" - ] - - cache-to = [ - "type=gha,mode=max" - ] -} - -# Production-specific target -target "app-prod" { - inherits = ["app"] - - args = { - APP_NAME = APP_NAME - NODE_ENV = "production" - VITE_COUCHDB_URL = "https://your-production-couchdb.com" - VITE_COUCHDB_USER = "admin" - VITE_COUCHDB_PASSWORD = "secure-production-password" - APP_BASE_URL = "https://your-domain.com" - VITE_GOOGLE_CLIENT_ID = "" - VITE_GITHUB_CLIENT_ID = "" - MAILGUN_API_KEY = "" - MAILGUN_DOMAIN = "" - MAILGUN_FROM_EMAIL = "" - } - - tags = [ - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:prod-${DOCKER_TAG}" : "rxminder:prod-${DOCKER_TAG}", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:prod-latest" : "rxminder:prod-latest", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:prod-${git_hash()}" : "rxminder:prod-${git_hash()}" - ] - - labels = { - "org.opencontainers.image.title" = "RxMinder Production" - "org.opencontainers.image.description" = "Medication reminder application - Production build" - "org.opencontainers.image.version" = DOCKER_TAG - "org.opencontainers.image.revision" = git_hash() - "org.opencontainers.image.source" = "https://github.com/username/rxminder" - "org.opencontainers.image.created" = timestamp() - "org.opencontainers.image.licenses" = "MIT" - "build.environment" = "production" - } -} - -# Development-specific target -target "app-dev" { - inherits = ["app"] - - args = { - APP_NAME = APP_NAME - NODE_ENV = "development" - VITE_COUCHDB_URL = "http://localhost:5984" - VITE_COUCHDB_USER = "admin" - VITE_COUCHDB_PASSWORD = "change-this-secure-password" - APP_BASE_URL = "http://localhost:8080" - VITE_GOOGLE_CLIENT_ID = "" - VITE_GITHUB_CLIENT_ID = "" - MAILGUN_API_KEY = "" - MAILGUN_DOMAIN = "" - MAILGUN_FROM_EMAIL = "" - } - - tags = [ - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:dev-${DOCKER_TAG}" : "rxminder:dev-${DOCKER_TAG}", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:dev-latest" : "rxminder:dev-latest", - notequal("", DOCKER_REGISTRY) ? "${DOCKER_REGISTRY}/rxminder:dev-${git_hash()}" : "rxminder:dev-${git_hash()}" - ] - - labels = { - "org.opencontainers.image.title" = "RxMinder Development" - "org.opencontainers.image.description" = "Medication reminder application - Development build" - "org.opencontainers.image.version" = DOCKER_TAG - "org.opencontainers.image.revision" = git_hash() - "org.opencontainers.image.source" = "https://github.com/username/rxminder" - "org.opencontainers.image.created" = timestamp() - "org.opencontainers.image.licenses" = "MIT" - "build.environment" = "development" - } -} - -# Local development target (single platform) -target "app-local" { - inherits = ["app-dev"] - - platforms = ["linux/amd64"] - - tags = [ - "rxminder:local", - "rxminder:dev-local" - ] - - output = ["type=docker"] -} - -# Testing target -target "app-test" { - inherits = ["app"] - - args = { - APP_NAME = "RxMinder-Test" - NODE_ENV = "test" - VITE_COUCHDB_URL = "http://localhost:5984" - VITE_COUCHDB_USER = "admin" - VITE_COUCHDB_PASSWORD = "test-password" - APP_BASE_URL = "http://localhost:8080" - } - - tags = [ - "rxminder:test", - "rxminder:test-${git_hash()}" - ] - - labels = { - "org.opencontainers.image.title" = "RxMinder Test" - "org.opencontainers.image.description" = "Medication reminder application - Test build" - "build.environment" = "test" - } - - output = ["type=docker"] -} diff --git a/docker/.dockerignore b/docker/.dockerignore deleted file mode 100644 index 3a67bb3..0000000 --- a/docker/.dockerignore +++ /dev/null @@ -1,79 +0,0 @@ -# Dependencies -node_modules -npm-debug.log* -yarn-debug.log* -yarn-error.log* -bun-debug.log* - -# Build output -dist -build - -# Environment files (security) -.env -.env.* -!.env.example - -# Development files -.vscode -.idea -*.swp -*.swo -*~ - -# Version control -.git -.gitignore -.gitattributes - -# Documentation -README.md -README_*.md -CHANGELOG.md -CONTRIBUTING.md -LICENSE -docs/ - -# Docker files (avoid recursion) -Dockerfile -docker-compose.yaml -.dockerignore - -# Scripts and testing (not needed in container) -scripts/ -tests/ -coverage/ -**/__tests__ -**/*.test.* -**/*.spec.* - -# Logs -logs -*.log - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Temporary files -tmp/ -temp/ -.tmp - -# CouchDB data -couchdb-data/ - -# Scripts (not needed in container) -setup.sh -deploy.sh -deploy-k8s.sh -validate-env.sh -validate-deployment.sh - -# Kubernetes manifests -k8s/ diff --git a/docker/.env.development b/docker/.env.development deleted file mode 100644 index 9fe060d..0000000 --- a/docker/.env.development +++ /dev/null @@ -1,18 +0,0 @@ -# Docker environment for development -# Generated on: 2025-09-09T03:27:30.600Z - -# Container Configuration -DOCKER_IMAGE=gitea-http.taildb3494.ts.net/will/meds:latest -CONTAINER_REGISTRY=gitea-http.taildb3494.ts.net -CONTAINER_REPOSITORY=will/meds -CONTAINER_TAG=latest - -# Application Configuration -APP_NAME=rxminder -NODE_ENV=development -PORT=5173 - -# Database Configuration -COUCHDB_URL=http://rxminder-couchdb-service:5984 -COUCHDB_USER=admin -COUCHDB_PASSWORD=L7tfqHyg0T4sIYiWK diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 47ed818..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,116 +0,0 @@ -# check=skip=SecretsUsedInArgOrEnv -# Multi-stage Docker build for RxMinder application -# Uses centralized configuration and follows security best practices - -# Build stage -FROM oven/bun:alpine AS builder - -# Install system dependencies for native modules -RUN apk add --no-cache python3 make gcc g++ musl-dev gettext - -# Create non-root user for security -RUN addgroup -g 1001 -S nodeuser && adduser -S nodeuser -u 1001 -G nodeuser - -# Create and set permissions for the working directory -RUN mkdir -p /app && chown -R nodeuser:nodeuser /app -WORKDIR /app -USER nodeuser - -# Copy package files first for better Docker layer caching -COPY --chown=nodeuser:nodeuser package.json ./ -COPY --chown=nodeuser:nodeuser bun.lock ./ - -# Install dependencies -RUN bun install --frozen-lockfile - -# Copy source code -COPY --chown=nodeuser:nodeuser . ./ - -# Build arguments for environment configuration -# Application Configuration -ARG APP_NAME=RxMinder -ARG APP_VERSION=1.0.0 -ARG APP_BASE_URL=http://localhost:5173 - -# Database Configuration -ARG VITE_COUCHDB_URL=http://localhost:5984 -ARG VITE_COUCHDB_USER=admin -ARG VITE_COUCHDB_PASSWORD=change-this-secure-password - -# Authentication Configuration -ARG JWT_SECRET=your-super-secret-jwt-key-change-in-production -ARG SESSION_SECRET=your-super-secret-session-key-change-in-production - -# Email Configuration (Optional) -ARG VITE_MAILGUN_API_KEY="" -ARG VITE_MAILGUN_DOMAIN="" -ARG VITE_MAILGUN_FROM_NAME="RxMinder" -ARG VITE_MAILGUN_FROM_EMAIL="" - -# OAuth Configuration (Optional) -ARG VITE_GOOGLE_CLIENT_ID="" -ARG VITE_GITHUB_CLIENT_ID="" - -# Feature Flags -ARG ENABLE_EMAIL_VERIFICATION=true -ARG ENABLE_OAUTH=true -ARG ENABLE_ADMIN_INTERFACE=true -ARG DEBUG_MODE=false - -# Build Environment -ARG NODE_ENV=production - -# Set environment variables for build process -# These are embedded into the static build at compile time -ENV VITE_APP_NAME=$APP_NAME -ENV APP_VERSION=$APP_VERSION -ENV APP_BASE_URL=$APP_BASE_URL -ENV VITE_COUCHDB_URL=$VITE_COUCHDB_URL -ENV VITE_COUCHDB_USER=$VITE_COUCHDB_USER -ENV VITE_COUCHDB_PASSWORD=$VITE_COUCHDB_PASSWORD -ENV JWT_SECRET=$JWT_SECRET -ENV VITE_MAILGUN_API_KEY=$VITE_MAILGUN_API_KEY -ENV VITE_MAILGUN_DOMAIN=$VITE_MAILGUN_DOMAIN -ENV VITE_MAILGUN_FROM_NAME=$VITE_MAILGUN_FROM_NAME -ENV VITE_MAILGUN_FROM_EMAIL=$VITE_MAILGUN_FROM_EMAIL -ENV VITE_GOOGLE_CLIENT_ID=$VITE_GOOGLE_CLIENT_ID -ENV VITE_GITHUB_CLIENT_ID=$VITE_GITHUB_CLIENT_ID -ENV ENABLE_EMAIL_VERIFICATION=$ENABLE_EMAIL_VERIFICATION -ENV ENABLE_OAUTH=$ENABLE_OAUTH -ENV ENABLE_ADMIN_INTERFACE=$ENABLE_ADMIN_INTERFACE -ENV DEBUG_MODE=$DEBUG_MODE -ENV NODE_ENV=$NODE_ENV - -# Process HTML template with APP_NAME -RUN envsubst '$APP_NAME' < index.html.template > index.html || cp index.html.template index.html - -# Build the application -RUN bun run build - -# Production stage - serve with nginx -FROM nginx:alpine - -# Install wget for health checks -RUN apk add --no-cache wget - -# Copy built files from builder stage -COPY --from=builder /app/dist /usr/share/nginx/html - -# Copy nginx configuration -COPY --from=builder /app/docker/nginx.conf /etc/nginx/conf.d/default.conf - -# Set proper permissions for nginx -RUN chown -R nginx:nginx /usr/share/nginx/html && \ - chown -R nginx:nginx /var/cache/nginx && \ - chown -R nginx:nginx /var/log/nginx && \ - chown -R nginx:nginx /etc/nginx/conf.d - -# Add health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1 - -# Expose port 80 -EXPOSE 80 - -# Start nginx (runs as nginx user by default in alpine) -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index ef178e6..0000000 --- a/docker/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# 🐳 Docker Configuration - -This directory contains all Docker and containerization-related files for RxMinder. - -## Files - -- **`Dockerfile`** - Multi-stage Docker build configuration with buildx support -- **`docker-compose.yaml`** - Service orchestration with multi-platform support -- **`docker-bake.hcl`** - Advanced buildx configuration for multi-platform builds -- **`nginx.conf`** - Production web server configuration -- **`.dockerignore`** - Files and directories to exclude from Docker build context - -## Docker Buildx Support - -This project now supports Docker Buildx for multi-platform builds (AMD64 and ARM64). - -### Quick Start with Buildx - -```bash -# Setup buildx builder (run once) -../scripts/buildx-helper.sh setup - -# Build for local platform only (faster for development) -../scripts/buildx-helper.sh build-local - -# Build for multiple platforms -../scripts/buildx-helper.sh build-multi - -# Build and push to registry -../scripts/buildx-helper.sh push docker.io/username latest - -# Build using Docker Bake (advanced) -../scripts/buildx-helper.sh bake -``` - -### Manual Buildx Commands - -```bash -# Create and use buildx builder -docker buildx create --name rxminder-builder --driver docker-container --bootstrap --use - -# Build for multiple platforms -docker buildx build --platform linux/amd64,linux/arm64 -t rxminder:latest --load . - -# Build with bake file -docker buildx bake -f docker-bake.hcl -``` - -## Traditional Usage - -From the project root directory: - -```bash -# Build and start services -docker compose -f docker/docker-compose.yaml up -d - -# View logs -docker compose -f docker/docker-compose.yaml logs - -# Stop services -docker compose -f docker/docker-compose.yaml down -``` - -## Build Process - -The Dockerfile uses a multi-stage build: - -1. **Builder stage**: Installs dependencies and builds the React app -2. **Production stage**: Serves the built app with nginx - -## Services - -- **frontend**: React application served by nginx -- **couchdb**: Database for medication and user data - -Both services include health checks and proper security configurations. diff --git a/docker/docker-bake.hcl b/docker/docker-bake.hcl deleted file mode 100644 index 61d5b98..0000000 --- a/docker/docker-bake.hcl +++ /dev/null @@ -1,101 +0,0 @@ -# Docker Bake file for advanced multi-platform builds -# Usage: docker buildx bake -f docker-bake.hcl - -variable "TAG" { - default = "latest" -} - -variable "REGISTRY" { - default = "" -} - -variable "VITE_COUCHDB_URL" { - default = "http://localhost:5984" -} - -variable "VITE_COUCHDB_USER" { - default = "admin" -} - -variable "VITE_COUCHDB_PASSWORD" { - default = "change-this-secure-password" -} - -variable "APP_BASE_URL" { - default = "http://localhost:8080" -} - -variable "VITE_GOOGLE_CLIENT_ID" { - default = "" -} - -variable "VITE_GITHUB_CLIENT_ID" { - default = "" -} - -group "default" { - targets = ["app"] -} - -target "app" { - dockerfile = "Dockerfile" - context = "." - platforms = [ - "linux/amd64", - "linux/arm64" - ] - - tags = [ - "${REGISTRY}rxminder:${TAG}", - "${REGISTRY}rxminder:latest" - ] - - args = { - # CouchDB Configuration - VITE_COUCHDB_URL = "${VITE_COUCHDB_URL}" - VITE_COUCHDB_USER = "${VITE_COUCHDB_USER}" - VITE_COUCHDB_PASSWORD = "${VITE_COUCHDB_PASSWORD}" - - # Application Configuration - APP_BASE_URL = "${APP_BASE_URL}" - - # OAuth Configuration (Optional) - VITE_GOOGLE_CLIENT_ID = "${VITE_GOOGLE_CLIENT_ID}" - VITE_GITHUB_CLIENT_ID = "${VITE_GITHUB_CLIENT_ID}" - - # Build environment - NODE_ENV = "production" - } - - # Advanced buildx features - cache-from = [ - "type=gha", - "type=registry,ref=${REGISTRY}rxminder:buildcache" - ] - - cache-to = [ - "type=gha,mode=max", - "type=registry,ref=${REGISTRY}rxminder:buildcache,mode=max" - ] - - # Attestations for supply chain security - attest = [ - "type=provenance,mode=max", - "type=sbom" - ] -} - -# Development target for faster local builds -target "dev" { - inherits = ["app"] - platforms = ["linux/amd64"] - tags = ["rxminder:dev"] - cache-from = ["type=gha"] - cache-to = ["type=gha,mode=max"] -} - -# Production target with registry push -target "prod" { - inherits = ["app"] - output = ["type=registry"] -} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml deleted file mode 100644 index 7643616..0000000 --- a/docker/docker-compose.yaml +++ /dev/null @@ -1,70 +0,0 @@ -services: - # Frontend service - frontend: - build: - context: .. - dockerfile: docker/Dockerfile - args: - # Application Configuration - - APP_NAME=${APP_NAME:-RxMinder} - # CouchDB Configuration - - VITE_COUCHDB_URL=${VITE_COUCHDB_URL:-http://couchdb:5984} - - VITE_COUCHDB_USER=${VITE_COUCHDB_USER:-admin} - - VITE_COUCHDB_PASSWORD=${VITE_COUCHDB_PASSWORD:-change-this-secure-password} - # Application Configuration - - APP_BASE_URL=${APP_BASE_URL:-http://localhost:8080} - # OAuth Configuration (Optional) - - VITE_GOOGLE_CLIENT_ID=${VITE_GOOGLE_CLIENT_ID:-} - - VITE_GITHUB_CLIENT_ID=${VITE_GITHUB_CLIENT_ID:-} - # Build Environment - - NODE_ENV=${NODE_ENV:-production} - # Enable buildx for multi-platform builds - platforms: - - linux/amd64 - - linux/arm64 - ports: - - '8080:80' - depends_on: - couchdb: - condition: service_healthy - restart: unless-stopped - # Health check for the frontend container - healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost/'] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - labels: - - 'monitoring=true' - - 'service=frontend' - - 'app=${APP_NAME:-meds}' - - # CouchDB service - couchdb: - image: couchdb:3.3.2 - volumes: - - ./couchdb-data:/opt/couchdb/data - environment: - - COUCHDB_USER=${COUCHDB_USER:-admin} - - COUCHDB_PASSWORD=${COUCHDB_PASSWORD:-change-this-secure-password} - ports: - - '5984:5984' - restart: unless-stopped - healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:5984/_up'] - interval: 30s - timeout: 10s - retries: 3 - labels: - - 'monitoring=true' - - 'service=couchdb' - - 'app=${APP_NAME:-meds}' - - # Redis service (commented out as per requirements) - # redis: - # image: redis:alpine - # restart: unless-stopped - # labels: - # - "monitoring=true" - # - "service=redis" diff --git a/docker/nginx.conf b/docker/nginx.conf deleted file mode 100644 index ca40632..0000000 --- a/docker/nginx.conf +++ /dev/null @@ -1,36 +0,0 @@ -server { - listen 80; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - # Enable gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; - - # Handle client-side routing - location / { - try_files $uri $uri/ /index.html; - } - - # Cache static assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Security headers - add_header X-Frame-Options DENY; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header Referrer-Policy "strict-origin-when-cross-origin"; - - # Health check endpoint - location /health { - access_log off; - return 200 "healthy\n"; - add_header Content-Type text/plain; - } -} diff --git a/docs/QUICK_DEPLOYMENT.md b/docs/QUICK_DEPLOYMENT.md deleted file mode 100644 index e1f0e57..0000000 --- a/docs/QUICK_DEPLOYMENT.md +++ /dev/null @@ -1,423 +0,0 @@ -# Quick Deployment Guide - -This guide provides simple, practical deployment options for the RxMinder application, ranging from local development to production deployment. - -## πŸš€ Instant Local Deployment - -For immediate testing and development, use the local Docker deployment: - -```bash -# Deploy locally with Docker (includes database) -make deploy-local - -# Access the application -open http://localhost:8080 - -# Stop when done -make stop-local -``` - -This deployment: - -- βœ… Includes CouchDB database -- βœ… Uses development configuration (safe defaults) -- βœ… Accessible at -- βœ… No additional configuration required -- βœ… Perfect for testing and development - -## πŸ“‹ Deployment Options Overview - -| Method | Use Case | Requirements | URL | Command | -| ----------------------- | ------------------------------ | -------------------- | ----------------------- | ----------------------------- | -| **Development Server** | Active development | Node.js/Bun | | `make dev` | -| **Local Docker** | Testing/Demo | Docker | | `make deploy-local` | -| **Production (Auto)** | Production with auto-secrets | Kubernetes cluster | Configured domain | `make deploy-prod-quick` | -| **Production (Manual)** | Production with custom secrets | Kubernetes + secrets | Configured domain | `make deploy-prod-configured` | - -## πŸ”§ Development Deployment - -### Option 1: Development Server (Fastest) - -```bash -# Install dependencies -make install - -# Start development server -make dev -``` - -- Hot reloading enabled -- Access at -- Uses mock database (in-memory) -- Best for active development - -### Option 2: Built Application Preview - -```bash -# Build and preview -make build -make preview -``` - -- Simulates production build -- Access at -- Uses mock database -- Good for testing build process - -## 🐳 Docker Deployment - -### Local Docker Deployment (Recommended for Testing) - -```bash -# Deploy with Docker Compose -make deploy-local -``` - -**What this includes:** - -- Frontend application (Nginx) -- CouchDB database with persistent data -- Health checks and monitoring -- Automatic restart policies - -**Access:** - -- Application: -- CouchDB Admin: - -**Management:** - -```bash -# View logs -docker-compose -f docker/docker-compose.yaml logs -f - -# Stop deployment -make stop-local - -# Restart deployment -make deploy-local - -# Clean up completely -make docker-clean -``` - -### Docker Compose Manual Commands - -```bash -# Start services -docker-compose -f docker/docker-compose.yaml up -d - -# Stop services -docker-compose -f docker/docker-compose.yaml down - -# View logs -docker-compose -f docker/docker-compose.yaml logs -f frontend -docker-compose -f docker/docker-compose.yaml logs -f couchdb - -# Rebuild and restart -docker-compose -f docker/docker-compose.yaml up -d --build -``` - -## ☸️ Kubernetes Deployment - -### Prerequisites - -1. **Kubernetes cluster** with kubectl configured -2. **Cluster admin access** to create namespaces -3. **Ingress controller** (optional, for external access) - -### Create Required Namespaces - -```bash -# Create development namespace -kubectl create namespace rxminder-dev - -# Create production namespace -kubectl create namespace rxminder-prod -``` - -### Development Deployment - -```bash -# Deploy to development environment -make deploy-dev - -# Check status -make status-dev - -# View logs -kubectl logs -n rxminder-dev -l app=rxminder - -# Remove deployment -make undeploy-dev -``` - -### Production Deployment - -#### Option A: Auto-Generated Secrets (Quick) - -```bash -# Deploy with automatically generated secrets -make deploy-prod-quick -``` - -#### Option B: Custom Configuration (Recommended) - -```bash -# Set your production secrets -export JWT_SECRET="your-secure-jwt-secret-here" -export SESSION_SECRET="your-secure-session-secret-here" -export VITE_COUCHDB_URL="https://your-couchdb-instance.com" - -# Deploy with your configuration -make deploy-prod-configured -``` - -#### Option C: Using Configuration Files - -```bash -# Generate production configuration -make generate-config-prod - -# Edit the generated files -nano .env.production - -# Deploy -make build-prod -make deploy-prod -``` - -### Kubernetes Management - -```bash -# Check deployment status -make status-prod -make status-dev - -# View differences before applying -make diff-prod -make diff-dev - -# Validate configurations -make validate-k8s - -# Remove deployments -make undeploy-prod -make undeploy-dev -make undeploy-all -``` - -## πŸ”’ Production Configuration - -### Required Environment Variables - -For production deployment, you must set: - -```bash -# Authentication (Required) -JWT_SECRET="your-secure-32-character-secret" -SESSION_SECRET="your-secure-32-character-secret" - -# Database (Required for persistence) -VITE_COUCHDB_URL="https://your-couchdb.com" -COUCHDB_USER="your-username" -COUCHDB_PASSWORD="your-password" -``` - -### Optional Configuration - -```bash -# Email notifications -MAILGUN_API_KEY="your-mailgun-key" -MAILGUN_DOMAIN="your-domain.com" - -# OAuth authentication -GOOGLE_CLIENT_ID="your-google-client-id" -GITHUB_CLIENT_ID="your-github-client-id" -``` - -### Generate Secure Secrets - -```bash -# Generate secure secrets -JWT_SECRET=$(openssl rand -base64 32) -SESSION_SECRET=$(openssl rand -base64 32) - -echo "JWT_SECRET=$JWT_SECRET" -echo "SESSION_SECRET=$SESSION_SECRET" -``` - -## πŸ” Troubleshooting - -### Common Issues - -#### Docker Issues - -```bash -# If deployment fails -make docker-clean -make deploy-local - -# If ports are in use -docker-compose -f docker/docker-compose.yaml down -# Wait a moment, then retry -make deploy-local -``` - -#### Build Issues - -```bash -# For production build failures -# Check that secrets are set -echo $JWT_SECRET -echo $SESSION_SECRET - -# Try development build instead -make build -``` - -#### Kubernetes Issues - -```bash -# If namespace doesn't exist -kubectl create namespace rxminder-dev -kubectl create namespace rxminder-prod - -# If deployment fails -kubectl get pods -n rxminder-dev -kubectl logs -n rxminder-dev -``` - -### Health Checks - -#### Local Docker Deployment - -```bash -# Check if services are running -docker-compose -f docker/docker-compose.yaml ps - -# Test frontend -curl http://localhost:8080 - -# Test CouchDB -curl http://localhost:5984/_up -``` - -#### Kubernetes Deployment - -```bash -# Check pod status -kubectl get pods -n rxminder-prod - -# Check service status -kubectl get services -n rxminder-prod - -# View logs -kubectl logs -n rxminder-prod -l app=rxminder -``` - -## πŸ“Š Monitoring - -### Docker Deployment - -```bash -# View real-time logs -docker-compose -f docker/docker-compose.yaml logs -f - -# Monitor resource usage -docker stats -``` - -### Kubernetes Deployment - -```bash -# Monitor pods -kubectl top pods -n rxminder-prod - -# View events -kubectl get events -n rxminder-prod --sort-by='.lastTimestamp' - -# Port forward for local access -kubectl port-forward -n rxminder-prod service/rxminder 8080:80 -``` - -## πŸ”„ Updates and Maintenance - -### Update Local Deployment - -```bash -# Pull latest changes -git pull - -# Rebuild and restart -make deploy-local -``` - -### Update Production Deployment - -```bash -# Pull latest changes -git pull - -# Build and deploy -make deploy-prod-quick - -# Or with custom configuration -make deploy-prod-configured -``` - -### Backup Data - -#### Docker CouchDB - -```bash -# CouchDB data is stored in docker/couchdb-data/ -# Backup this directory -tar -czf couchdb-backup-$(date +%Y%m%d).tar.gz docker/couchdb-data/ -``` - -#### Kubernetes CouchDB - -```bash -# Access CouchDB pod -kubectl exec -n rxminder-prod -it -- bash - -# Use CouchDB backup tools -# Or backup persistent volume -``` - -## πŸš€ Quick Commands Reference - -```bash -# Development -make dev # Start development server -make build # Build for development -make test # Run tests - -# Local deployment -make deploy-local # Deploy with Docker -make stop-local # Stop Docker deployment - -# Production deployment -make deploy-prod-quick # Deploy with auto-generated secrets -make deploy-prod-configured # Deploy with custom secrets - -# Management -make help # Show all available commands -make validate-k8s # Validate Kubernetes configs -make docker-clean # Clean up Docker resources -``` - ---- - -**Need more detailed information?** - -- [Production Build Guide](deployment/PRODUCTION_BUILD.md) - Detailed production configuration -- [Docker Configuration](deployment/DOCKER_IMAGE_CONFIGURATION.md) - Docker setup details -- [Database Service](development/DATABASE.md) - Database configuration -- [Main README](../README.md) - Complete project overview - ---- - -**Last Updated:** January 2024 -**Status:** Ready for deployment diff --git a/docs/deployment/DEPLOYMENT.md b/docs/deployment/DEPLOYMENT.md deleted file mode 100644 index 63d5463..0000000 --- a/docs/deployment/DEPLOYMENT.md +++ /dev/null @@ -1,538 +0,0 @@ -# Deployment Guide - -## πŸš€ Complete Deployment Guide for Medication Reminder App - -### **Prerequisites** - -#### **System Requirements** - -- Docker 20.10+ and Docker Compose 2.0+ -- 2GB RAM minimum, 4GB recommended -- 10GB disk space for application and data -- Linux/macOS/Windows with WSL2 - -#### **Required Accounts** - -- [Mailgun Account](https://mailgun.com) for email services -- Domain name for production deployment (optional) -- SSL certificate for HTTPS (recommended) - -### **Environment Setup** - -#### **1. Clone Repository** - -```bash -git clone -cd meds -``` - -#### **2. Configure Environment** - -```bash -# Copy template -cp .env.example .env - -# Edit with your credentials -nano .env -``` - -**Required Variables:** - -```bash -# Application Configuration -APP_BASE_URL=https://yourdomain.com - -# CouchDB Configuration -COUCHDB_USER=admin -COUCHDB_PASSWORD=super-secure-password-123! -VITE_COUCHDB_URL=http://couchdb:5984 -VITE_COUCHDB_USER=admin -VITE_COUCHDB_PASSWORD=super-secure-password-123! - -# Mailgun Configuration -MAILGUN_API_KEY=key-1234567890abcdef1234567890abcdef -MAILGUN_DOMAIN=mg.yourdomain.com -MAILGUN_FROM_EMAIL=noreply@yourdomain.com - -# Production Settings -NODE_ENV=production -``` - -### **Local Development Deployment** - -#### **Quick Start** - -```bash -# Automated setup -./setup.sh - -# Manual setup -bun install -docker compose up -d -bun run seed-production.js -``` - -#### **Development URLs** - -- Frontend: http://localhost:8080 -- CouchDB: http://localhost:5984 -- Admin Panel: http://localhost:5984/\_utils - -### **Production Deployment** - -#### **Method 1: Automated Script** - -```bash -# Secure deployment with validation -./deploy.sh production -``` - -#### **Method 2: Manual Docker Compose** - -```bash -# Build images -docker compose build --no-cache - -# Start services -docker compose up -d - -# Seed database -node seed-production.js - -# Verify deployment -bun test-production.js -``` - -#### **Method 3: Docker Swarm** - -```bash -# Initialize swarm -docker swarm init - -# Deploy stack -docker stack deploy -c docker/docker-compose.yaml meds-stack - -# Scale services -docker service scale meds-stack_frontend=3 -``` - -### **Cloud Platform Deployments** - -#### **AWS EC2 Deployment** - -**1. Launch EC2 Instance** - -```bash -# Amazon Linux 2 AMI -# Instance type: t3.medium or larger -# Security group: Allow ports 22, 80, 443, 8080 -``` - -**2. Install Dependencies** - -```bash -# Connect to instance -ssh -i your-key.pem ec2-user@your-instance-ip - -# Install Docker -sudo yum update -y -sudo yum install -y docker -sudo service docker start -sudo usermod -a -G docker ec2-user - -# Install Docker Compose -sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -sudo chmod +x /usr/local/bin/docker-compose -``` - -**3. Deploy Application** - -```bash -# Clone and configure -git clone -cd meds -cp .env.example .env -# Edit .env with production values - -# Deploy -./deploy.sh production -``` - -#### **Google Cloud Platform Deployment** - -**1. Cloud Run Deployment** - -```bash -# Build and push image -gcloud builds submit --tag gcr.io/PROJECT-ID/meds-app - -# Deploy service -gcloud run deploy meds-app \ - --image gcr.io/PROJECT-ID/meds-app \ - --platform managed \ - --region us-central1 \ - --set-env-vars COUCHDB_URL=your-couchdb-url \ - --set-env-vars MAILGUN_API_KEY=your-key \ - --allow-unauthenticated -``` - -**2. Compute Engine Deployment** - -```bash -# Create instance -gcloud compute instances create meds-server \ - --image-family debian-11 \ - --image-project debian-cloud \ - --machine-type e2-medium \ - --tags http-server,https-server - -# SSH and install -gcloud compute ssh meds-server -# Follow standard installation steps -``` - -#### **Digital Ocean Deployment** - -**1. Droplet Setup** - -```bash -# Create droplet with Docker pre-installed -# Or install Docker manually on Ubuntu droplet - -# Connect and deploy -ssh root@your-droplet-ip -git clone -cd meds -./setup.sh -./deploy.sh production -``` - -**2. App Platform Deployment** - -```bash -# Create app.yaml -version: 1 -services: -- name: meds-app - source_dir: / - github: - repo: your-username/meds - branch: main - build_command: bun run build - environment_slug: node-js - instance_count: 1 - instance_size_slug: basic-xxs - envs: - - key: COUCHDB_URL - value: ${COUCHDB_URL} - - key: MAILGUN_API_KEY - value: ${MAILGUN_API_KEY} - -# Deploy -doctl apps create --spec app.yaml -``` - -### **Kubernetes Deployment** - -#### **Method 1: Automated Deployment Script (Recommended)** - -```bash -# Configure environment -cp .env.example .env -# Edit .env with your settings: -# INGRESS_HOST=app.meds.192.168.1.100.nip.io # For local cluster -# INGRESS_HOST=meds.yourdomain.com # For production - -# Deploy with environment substitution -./deploy-k8s.sh - -# Check deployment status -./deploy-k8s.sh --status - -# Deploy with custom environment file -./deploy-k8s.sh --env .env.production - -# Preview deployment (dry run) -./deploy-k8s.sh --dry-run -``` - -#### **Method 2: Manual Deployment** - -#### **1. Create Namespace and Secrets** - -```bash -# Create namespace -kubectl create namespace meds-app - -# Create secrets -kubectl create secret generic meds-secrets \ - --from-literal=couchdb-user=admin \ - --from-literal=couchdb-password=secure-password \ - --from-literal=mailgun-api-key=your-api-key \ - --namespace meds-app -``` - -#### **2. Deploy Services** - -```bash -# Apply Kubernetes manifests -kubectl apply -f k8s/ --namespace meds-app - -# Check deployment status -kubectl get pods -n meds-app -kubectl get services -n meds-app -``` - -#### **3. Configure Ingress (Manual)** - -```yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: meds-ingress - namespace: meds-app - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: letsencrypt-prod -spec: - tls: - - hosts: - - meds.yourdomain.com - secretName: meds-tls - rules: - - host: meds.yourdomain.com # Update this to your domain - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: meds-frontend - port: - number: 80 -``` - -### **SSL/HTTPS Configuration** - -#### **Let's Encrypt with Nginx** - -```bash -# Install certbot -sudo apt-get install certbot python3-certbot-nginx - -# Get certificate -sudo certbot --nginx -d yourdomain.com - -# Auto-renewal -sudo crontab -e -# Add: 0 12 * * * /usr/bin/certbot renew --quiet -``` - -#### **Cloudflare SSL** - -```bash -# Update docker/nginx.conf for Cloudflare -# Set ssl_certificate and ssl_certificate_key -# Configure Cloudflare for Full (Strict) SSL -``` - -### **Database Backup and Recovery** - -#### **CouchDB Backup** - -```bash -# Create backup script -#!/bin/bash -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_DIR="/backup/couchdb" - -# Backup all databases -curl -X GET http://admin:password@localhost:5984/_all_dbs | \ -jq -r '.[]' | while read db; do - curl -X GET "http://admin:password@localhost:5984/$db/_all_docs?include_docs=true" \ - > "$BACKUP_DIR/${db}_${DATE}.json" -done -``` - -#### **Automated Backups** - -```bash -# Add to crontab -0 2 * * * /opt/meds/backup-couchdb.sh - -# Upload to cloud storage -aws s3 cp /backup/couchdb/ s3://your-backup-bucket/ --recursive -``` - -### **Monitoring and Logging** - -#### **Health Checks** - -```bash -# Application health -curl -f http://localhost:8080/health - -# CouchDB health -curl -f http://admin:password@localhost:5984/_up - -# Docker container health -docker compose ps -``` - -#### **Log Management** - -```bash -# View logs -docker compose logs -f frontend -docker compose logs -f couchdb - -# Log rotation -# Configure in docker/docker-compose.yaml: -logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" -``` - -#### **Performance Monitoring** - -```bash -# Resource usage -docker stats - -# Application metrics -# Implement custom metrics endpoint -# Use Prometheus/Grafana for monitoring -``` - -### **Scaling and Load Balancing** - -#### **Horizontal Scaling** - -```bash -# Scale frontend containers -docker compose up -d --scale frontend=3 - -# Load balancer configuration -# Use nginx, HAProxy, or cloud load balancer -``` - -#### **Database Scaling** - -```bash -# CouchDB clustering -# Configure multiple CouchDB nodes -# Set up replication between nodes -``` - -### **Security Hardening** - -#### **Firewall Configuration** - -```bash -# UFW (Ubuntu) -sudo ufw allow 22/tcp -sudo ufw allow 80/tcp -sudo ufw allow 443/tcp -sudo ufw deny 5984/tcp # CouchDB admin (internal only) -sudo ufw enable -``` - -#### **Container Security** - -```bash -# Run security scan -docker scout cves meds-frontend:latest - -# Update base images regularly -docker compose build --no-cache -``` - -### **Troubleshooting** - -#### **Common Issues** - -**1. Environment Variables Not Loading** - -```bash -# Check file format -cat -A .env - -# Verify Docker Compose config -docker compose config -``` - -**2. Database Connection Issues** - -```bash -# Test CouchDB connection -curl -u admin:password http://localhost:5984/ - -# Check container logs -docker compose logs couchdb -``` - -**3. Email Not Sending** - -```bash -# Verify Mailgun configuration -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' -``` - -**4. Frontend Build Failures** - -```bash -# Clear cache and rebuild -docker compose build --no-cache frontend -``` - -### **Maintenance** - -#### **Regular Tasks** - -- Update dependencies monthly -- Rotate credentials quarterly -- Backup database daily -- Monitor disk space weekly -- Review security logs daily - -#### **Update Process** - -```bash -# 1. Backup current deployment -./backup.sh - -# 2. Pull latest changes -git pull origin main - -# 3. Update dependencies -bun install - -# 4. Rebuild and deploy -docker compose build --no-cache -docker compose up -d - -# 5. Verify deployment -bun test-production.js -``` - -### **Support and Documentation** - -#### **Getting Help** - -- GitHub Issues: Create issue for bugs/features -- Documentation: Check README.md and docs/ -- Community: Join our Discord/Slack channel - -#### **Professional Support** - -- Enterprise support available -- Custom deployment assistance -- Security auditing services -- Performance optimization consulting diff --git a/docs/deployment/DOCKER_IMAGE_CONFIGURATION.md b/docs/deployment/DOCKER_IMAGE_CONFIGURATION.md deleted file mode 100644 index 085ba47..0000000 --- a/docs/deployment/DOCKER_IMAGE_CONFIGURATION.md +++ /dev/null @@ -1,265 +0,0 @@ -# 🐳 Docker Image Configuration - -## Overview - -RxMinder now supports configurable Docker images via environment variables, enabling flexible deployment across different registries, environments, and versions. - -## 🎯 Docker Image Variable - -### **DOCKER_IMAGE** - -The complete Docker image specification including registry, repository, and tag. - -**Format:** `[registry/]repository:tag` - -## 🌐 Registry Examples - -### Public Registries - -#### Docker Hub - -```bash -# Official image on Docker Hub -DOCKER_IMAGE=rxminder/rxminder:latest -DOCKER_IMAGE=rxminder/rxminder:v1.2.0 -DOCKER_IMAGE=rxminder/rxminder:stable -``` - -#### GitHub Container Registry (ghcr.io) - -```bash -# GitHub Packages -DOCKER_IMAGE=ghcr.io/username/rxminder:latest -DOCKER_IMAGE=ghcr.io/organization/rxminder:v1.2.0 -DOCKER_IMAGE=ghcr.io/username/rxminder:dev-branch -``` - -#### GitLab Container Registry - -```bash -# GitLab Registry -DOCKER_IMAGE=registry.gitlab.com/username/rxminder:latest -DOCKER_IMAGE=registry.gitlab.com/group/rxminder:production -``` - -### Private/Self-Hosted Registries - -#### Gitea Registry - -```bash -# Current default (Gitea) -DOCKER_IMAGE=gitea-http.taildb3494.ts.net/will/meds:latest -DOCKER_IMAGE=gitea-http.taildb3494.ts.net/will/meds:v1.2.0 -``` - -#### Harbor Registry - -```bash -# Harbor enterprise registry -DOCKER_IMAGE=harbor.company.com/rxminder/rxminder:latest -DOCKER_IMAGE=harbor.company.com/rxminder/rxminder:production -``` - -#### Local Registry - -```bash -# Local development registry -DOCKER_IMAGE=localhost:5000/rxminder:latest -DOCKER_IMAGE=registry.local:5000/rxminder:dev -``` - -### Cloud Provider Registries - -#### AWS Elastic Container Registry (ECR) - -```bash -# AWS ECR -DOCKER_IMAGE=123456789012.dkr.ecr.us-west-2.amazonaws.com/rxminder:latest -DOCKER_IMAGE=123456789012.dkr.ecr.us-west-2.amazonaws.com/rxminder:v1.2.0 -``` - -#### Google Container Registry (GCR) - -```bash -# Google Cloud Registry -DOCKER_IMAGE=gcr.io/project-id/rxminder:latest -DOCKER_IMAGE=us.gcr.io/project-id/rxminder:production -``` - -#### Azure Container Registry (ACR) - -```bash -# Azure Container Registry -DOCKER_IMAGE=myregistry.azurecr.io/rxminder:latest -DOCKER_IMAGE=myregistry.azurecr.io/rxminder:stable -``` - -## 🏷️ Tagging Strategies - -### Environment-Based Tagging - -```bash -# Development -DOCKER_IMAGE=myregistry.com/rxminder:dev -DOCKER_IMAGE=myregistry.com/rxminder:develop-20250906 - -# Staging -DOCKER_IMAGE=myregistry.com/rxminder:staging -DOCKER_IMAGE=myregistry.com/rxminder:release-candidate - -# Production -DOCKER_IMAGE=myregistry.com/rxminder:stable -DOCKER_IMAGE=myregistry.com/rxminder:v1.2.0 -``` - -### Git-Based Tagging - -```bash -# Branch-based -DOCKER_IMAGE=myregistry.com/rxminder:main -DOCKER_IMAGE=myregistry.com/rxminder:feature-auth - -# Commit-based -DOCKER_IMAGE=myregistry.com/rxminder:sha-abc1234 -DOCKER_IMAGE=myregistry.com/rxminder:pr-123 -``` - -### Semantic Versioning - -```bash -# Semantic versions -DOCKER_IMAGE=myregistry.com/rxminder:v1.0.0 -DOCKER_IMAGE=myregistry.com/rxminder:v1.2.3-beta -DOCKER_IMAGE=myregistry.com/rxminder:v2.0.0-rc1 -``` - -## πŸŽͺ Environment-Specific Configurations - -### Development (.env) - -```bash -APP_NAME=rxminder-dev -DOCKER_IMAGE=localhost:5000/rxminder:dev -STORAGE_CLASS=local-path -STORAGE_SIZE=5Gi -INGRESS_HOST=rxminder-dev.local -``` - -### Staging (.env.staging) - -```bash -APP_NAME=rxminder-staging -DOCKER_IMAGE=myregistry.com/rxminder:staging -STORAGE_CLASS=longhorn -STORAGE_SIZE=10Gi -INGRESS_HOST=staging.rxminder.company.com -``` - -### Production (.env.production) - -```bash -APP_NAME=rxminder -DOCKER_IMAGE=myregistry.com/rxminder:v1.2.0 # Fixed version for stability -STORAGE_CLASS=fast-ssd -STORAGE_SIZE=50Gi -INGRESS_HOST=rxminder.company.com -``` - -## πŸš€ CI/CD Integration - -### GitHub Actions Example - -```yaml -# .github/workflows/deploy.yml -- name: Deploy to Kubernetes - env: - DOCKER_IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }} - run: | - echo "DOCKER_IMAGE=${DOCKER_IMAGE}" >> .env - ./scripts/k8s-deploy-template.sh deploy -``` - -### GitLab CI Example - -```yaml -# .gitlab-ci.yml -deploy: - variables: - DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - script: - - echo "DOCKER_IMAGE=${DOCKER_IMAGE}" >> .env - - ./scripts/k8s-deploy-template.sh deploy -``` - -## πŸ”’ Registry Authentication - -### Docker Registry Secrets - -```bash -# Create registry secret for private registries -kubectl create secret docker-registry regcred \ - --docker-server=myregistry.com \ - --docker-username=username \ - --docker-password=password \ - --docker-email=email@company.com - -# Update deployment to use the secret -# (Add imagePullSecrets to deployment template if needed) -``` - -### Cloud Provider Authentication - -```bash -# AWS ECR -aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-west-2.amazonaws.com - -# Google GCR -gcloud auth configure-docker - -# Azure ACR -az acr login --name myregistry -``` - -## πŸ’‘ Best Practices - -### Production Recommendations - -- βœ… **Use specific tags** (not `:latest`) for production -- βœ… **Pin to exact versions** for stability -- βœ… **Use semantic versioning** for releases -- βœ… **Separate registries** for different environments -- βœ… **Enable vulnerability scanning** on registries - -### Development Workflow - -- βœ… **Use `:dev` or `:latest`** for development -- βœ… **Branch-based tags** for feature development -- βœ… **Local registries** for fast iteration -- βœ… **Automated builds** on code changes - -### Security Considerations - -- βœ… **Private registries** for proprietary code -- βœ… **Registry authentication** properly configured -- βœ… **Image scanning** for vulnerabilities -- βœ… **Supply chain security** with signed images - -## 🎭 Example Deployments - -### Multi-Environment Setup - -```bash -# Development -export DOCKER_IMAGE=localhost:5000/rxminder:dev -./scripts/k8s-deploy-template.sh deploy - -# Staging -export DOCKER_IMAGE=registry.company.com/rxminder:staging -./scripts/k8s-deploy-template.sh deploy - -# Production -export DOCKER_IMAGE=registry.company.com/rxminder:v1.2.0 -./scripts/k8s-deploy-template.sh deploy -``` - -This flexible Docker image configuration makes RxMinder truly **portable** and **CI/CD-ready** across any container registry and deployment environment! diff --git a/docs/deployment/GITEA_SETUP.md b/docs/deployment/GITEA_SETUP.md deleted file mode 100644 index 21616b1..0000000 --- a/docs/deployment/GITEA_SETUP.md +++ /dev/null @@ -1,242 +0,0 @@ -# 🦌 Gitea CI/CD Setup Complete! - -Your RxMinder app now has comprehensive Gitea Actions CI/CD support! Here's what's been created: - -## πŸ“ New Files Structure - -``` -.gitea/ -β”œβ”€β”€ workflows/ -β”‚ └── ci-cd.yml # Main CI/CD workflow -β”œβ”€β”€ docker-compose.ci.yml # CI-specific compose override -β”œβ”€β”€ gitea-bake.hcl # Gitea-optimized buildx config -└── README.md # Detailed Gitea configuration guide - -scripts/ -β”œβ”€β”€ gitea-deploy.sh # Gitea-specific deployment script -└── gitea-helper.sh # Comprehensive Gitea operations helper -``` - -## πŸš€ Quick Start - -### 1. **Setup Environment Configuration** - -```bash -# Copy the example environment file and customize -cp .env.example .env - -# Edit .env with your registry and configuration: -CONTAINER_REGISTRY=gitea.yourdomain.com -CONTAINER_REPOSITORY=username/rxminder -GITEA_REGISTRY=gitea.yourdomain.com -GITEA_REPOSITORY=username/rxminder -``` - -### 2. **Setup Gitea Repository** - -```bash -# Configure in Gitea Repository Settings β†’ Actions - -# Required Secrets: -GITEA_TOKEN # Personal access token with package write permissions -VITE_COUCHDB_PASSWORD # CouchDB password -DEPLOYMENT_WEBHOOK_URL # Optional: deployment notifications - -# Repository Variables (optional - will use .env defaults): -GITEA_REGISTRY # Override registry from .env -VITE_COUCHDB_URL # http://localhost:5984 -VITE_COUCHDB_USER # admin -APP_BASE_URL # http://localhost:8080 -``` - -### 3. **Local Development with Gitea** - -```bash -# Setup Gitea buildx builder -bun run gitea:setup - -# Build for local development -bun run gitea:build-local - -# Run tests -bun run gitea:test - -# Check status -bun run gitea:status -``` - -### 4. **Production Deployment** - -```bash -# Build and push to registry -export GITEA_TOKEN=your_token -export GITEA_REGISTRY=your-gitea.com -export GITEA_REPOSITORY=username/rxminder - -bun run gitea:build-prod v1.0.0 - -# Deploy to production -bun run gitea:deploy production v1.0.0 -``` - -## πŸ”§ Gitea Actions Features - -### **Multi-Platform Builds** - -- βœ… AMD64 (Intel/AMD processors) -- βœ… ARM64 (Apple Silicon, AWS Graviton) -- βœ… Optimized layer caching -- βœ… Registry-based build cache - -### **Security & Quality** - -- βœ… Trivy vulnerability scanning -- βœ… Supply chain attestations (SBOM, provenance) -- βœ… Dependency auditing -- βœ… Lint and type checking - -### **Deployment Options** - -- βœ… Docker Compose deployment -- βœ… Kubernetes deployment -- βœ… Staging environment support -- βœ… Health checks and monitoring - -### **Automation** - -- βœ… Automatic builds on push/PR -- βœ… Multi-environment deployments -- βœ… Image cleanup and maintenance -- βœ… Deployment notifications - -## πŸ“‹ Available Commands - -### **Gitea Helper Script** - -```bash -./scripts/gitea-helper.sh setup # Setup buildx for Gitea -./scripts/gitea-helper.sh build-local # Local development build -./scripts/gitea-helper.sh build-multi # Multi-platform build -./scripts/gitea-helper.sh build-staging # Staging build -./scripts/gitea-helper.sh build-prod # Production build -./scripts/gitea-helper.sh test # Run all tests -./scripts/gitea-helper.sh deploy # Deploy to environment -./scripts/gitea-helper.sh status # Show CI/CD status -./scripts/gitea-helper.sh cleanup # Cleanup builders/images -``` - -### **Package.json Scripts** - -```bash -bun run gitea:setup # Setup Gitea buildx -bun run gitea:build # Multi-platform build -bun run gitea:build-local # Local development -bun run gitea:build-staging # Staging build -bun run gitea:build-prod # Production build -bun run gitea:test # Run tests -bun run gitea:deploy # Deploy application -bun run gitea:status # Check status -bun run gitea:cleanup # Cleanup -``` - -## 🎯 Workflow Triggers - -### **Automatic Triggers** - -- **Push to main/develop**: Full build, test, and deploy -- **Pull Request**: Build, test, and security scan -- **Manual dispatch**: On-demand deployment - -### **Environment-Specific** - -- **Development**: Fast single-platform builds -- **Staging**: Full testing with staging configs -- **Production**: Multi-platform with attestations - -## πŸ”’ Security Features - -### **Image Security** - -- Vulnerability scanning with Trivy -- Base image security updates -- Minimal attack surface -- Supply chain attestations - -### **Secrets Management** - -- Gitea-native secrets storage -- Environment-specific variables -- Token rotation support -- Secure registry authentication - -## πŸ“Š Monitoring & Notifications - -### **Health Checks** - -- Frontend application health -- Database connectivity -- Service dependency checks -- Container resource monitoring - -### **Notifications** - -- Deployment success/failure alerts -- Security scan results -- Build status updates -- Custom webhook integration - -## πŸš€ Next Steps - -1. **Configure Gitea Repository**: - - Enable Actions in repository settings - - Add required secrets and variables - - Configure container registry - -2. **Set up Gitea Runner**: - - Install and configure Gitea Actions runner - - Ensure Docker and buildx support - - Configure appropriate labels - -3. **Test the Pipeline**: - - ```bash - # Push to trigger the workflow - git add . - git commit -m "Setup Gitea CI/CD" - git push origin main - ``` - -4. **Customize for Your Environment**: - - Update registry URLs in `.gitea/gitea-bake.hcl` - - Modify deployment targets in `scripts/gitea-deploy.sh` - - Configure environment-specific variables - -## πŸ”„ Migration Notes - -- βœ… **Fully compatible** with existing Docker Buildx setup -- βœ… **No breaking changes** to development workflow -- βœ… **Parallel support** with GitHub Actions if needed -- βœ… **Easy rollback** - simply delete `.gitea/` directory - -Your RxMinder app is now ready for professional-grade CI/CD with Gitea! πŸŽ‰ - -## πŸ“ž Troubleshooting - -### Common Issues: - -1. **Build failures**: Check Gitea runner has Docker buildx -2. **Registry push errors**: Verify GITEA_TOKEN permissions -3. **Deployment issues**: Check environment variables and secrets - -### Debug Commands: - -```bash -# Check Gitea environment -./scripts/gitea-helper.sh status - -# Test local build -./scripts/gitea-helper.sh build-local - -# Verify registry login -docker login your-gitea.com -``` diff --git a/docs/deployment/PRODUCTION_BUILD.md b/docs/deployment/PRODUCTION_BUILD.md deleted file mode 100644 index a225c57..0000000 --- a/docs/deployment/PRODUCTION_BUILD.md +++ /dev/null @@ -1,407 +0,0 @@ -# Production Build Guide - -This guide explains how to properly configure and build the RxMinder application for production deployment. - -## Overview - -The RxMinder application has strict security validations that prevent production builds with default or insecure configurations. This ensures that production deployments are properly secured. - -## Build Commands - -### Development Build (Default) - -```bash -make build # Builds with development configuration -NODE_ENV=development bun run build -``` - -### Production Build - -```bash -make build-prod # Builds with production configuration -bun run build # Requires proper production environment setup -``` - -## Production Configuration Requirements - -### Required Environment Variables - -Before running a production build, you must set the following environment variables: - -#### Authentication Secrets - -```bash -# Required: Change these from defaults -JWT_SECRET=your-secure-jwt-secret-here -SESSION_SECRET=your-secure-session-secret-here -``` - -#### Database Configuration - -```bash -# Required for production deployments -VITE_COUCHDB_URL=https://your-couchdb-instance.com -COUCHDB_USER=your-couchdb-username -COUCHDB_PASSWORD=your-couchdb-password -``` - -#### Email Configuration (Optional) - -```bash -# Mailgun configuration -MAILGUN_API_KEY=your-mailgun-api-key -MAILGUN_DOMAIN=your-domain.com -MAILGUN_FROM_NAME="Your App Name" -MAILGUN_FROM_EMAIL=noreply@your-domain.com -``` - -#### OAuth Configuration (Optional) - -```bash -# Google OAuth -GOOGLE_CLIENT_ID=your-google-client-id -GOOGLE_CLIENT_SECRET=your-google-client-secret - -# GitHub OAuth -GITHUB_CLIENT_ID=your-github-client-id -GITHUB_CLIENT_SECRET=your-github-client-secret -``` - -## Configuration Methods - -### Method 1: Environment Variables - -Set environment variables directly: - -```bash -export JWT_SECRET="your-secure-jwt-secret" -export SESSION_SECRET="your-secure-session-secret" -export VITE_COUCHDB_URL="https://your-couchdb.com" -make build-prod -``` - -### Method 2: .env File - -Create a `.env.production` file: - -```bash -# Copy and modify the example -cp .env.example .env.production - -# Edit with your production values -nano .env.production -``` - -Then build: - -```bash -NODE_ENV=production make build-prod -``` - -### Method 3: Generate Production Config - -Use the unified configuration system: - -```bash -# Generate production configuration -make generate-config-prod - -# Then build -make build-prod -``` - -## Security Validations - -The build process validates the following: - -### Critical Errors (Will Fail Build) - -- `JWT_SECRET` must not be the default value in production -- `SESSION_SECRET` must not be the default value in production - -### Warnings (Will Show But Allow Build) - -- Missing OAuth client IDs (OAuth will be disabled) -- Missing email configuration (Email features disabled) -- Using default database URL (Will use mock database) - -## Production Build Process - -### 1. Validate Configuration - -```bash -# Check current configuration -make config-debug - -# Validate configuration -make validate-config -``` - -### 2. Set Production Environment - -```bash -export NODE_ENV=production -``` - -### 3. Configure Secrets - -```bash -# Generate secure secrets -export JWT_SECRET=$(openssl rand -base64 32) -export SESSION_SECRET=$(openssl rand -base64 32) -``` - -### 4. Build Application - -```bash -make build-prod -``` - -### 5. Verify Build - -```bash -# Check dist directory -ls -la dist/ - -# Test build locally -make preview -``` - -## Docker Production Build - -### Using Docker Compose - -```bash -# Build production image -docker-compose -f docker/docker-compose.prod.yml build - -# Deploy with production configuration -docker-compose -f docker/docker-compose.prod.yml up -d -``` - -### Using Makefile - -```bash -# Build production Docker images -make docker-build - -# With production environment -NODE_ENV=production make docker-build -``` - -## Kubernetes Production Deployment - -### 1. Generate Kubernetes Configuration - -```bash -# Generate production K8s configs -make generate-config-prod -``` - -### 2. Apply Configuration - -```bash -# Deploy to production -make deploy-prod - -# Check deployment status -make status-prod -``` - -### 3. Verify Deployment - -```bash -# Check differences before applying -make diff-prod - -# Validate configurations -make validate-k8s -``` - -## Environment-Specific Builds - -### Development - -```bash -NODE_ENV=development make build -# Uses development defaults, allows insecure configurations -``` - -### Staging - -```bash -NODE_ENV=staging make build-prod -# Uses staging configuration with production validations -``` - -### Production - -```bash -NODE_ENV=production make build-prod -# Requires all production security validations -``` - -## Troubleshooting - -### Error: "JWT_SECRET must be changed in production" - -```bash -# Solution: Set a secure JWT secret -export JWT_SECRET=$(openssl rand -base64 32) -export SESSION_SECRET=$(openssl rand -base64 32) -make build-prod -``` - -### Error: "Configuration validation failed" - -```bash -# Check what's missing -make validate-config - -# Debug current configuration -make config-debug - -# Generate complete configuration -make generate-config-prod -``` - -### Build Succeeds But Features Missing - -Check warnings in build output: - -- Missing OAuth: Set `GOOGLE_CLIENT_ID` and `GITHUB_CLIENT_ID` -- Missing email: Set `MAILGUN_API_KEY` and `MAILGUN_DOMAIN` -- Mock database: Set `VITE_COUCHDB_URL` to real database - -## Security Best Practices - -### Secret Management - -1. **Never commit secrets** to version control -2. **Use environment variables** or secure secret management -3. **Rotate secrets regularly** in production -4. **Use different secrets** for each environment - -### Database Security - -1. **Use HTTPS** for CouchDB connections -2. **Configure authentication** with strong passwords -3. **Limit database access** to application only -4. **Regular backups** and security updates - -### OAuth Security - -1. **Restrict redirect URIs** to your domains only -2. **Use HTTPS** for OAuth redirects -3. **Regularly review** OAuth application permissions -4. **Monitor OAuth usage** for suspicious activity - -## CI/CD Integration - -### GitHub Actions Example - -```yaml -- name: Build Production - env: - JWT_SECRET: ${{ secrets.JWT_SECRET }} - SESSION_SECRET: ${{ secrets.SESSION_SECRET }} - VITE_COUCHDB_URL: ${{ secrets.COUCHDB_URL }} - run: make build-prod -``` - -### GitLab CI Example - -```yaml -build:production: - variables: - NODE_ENV: 'production' - script: - - export JWT_SECRET="$PRODUCTION_JWT_SECRET" - - export SESSION_SECRET="$PRODUCTION_SESSION_SECRET" - - make build-prod -``` - -## Performance Optimization - -### Bundle Analysis - -```bash -# Build with bundle analysis -ANALYZE=true make build-prod - -# Check bundle size -ls -lh dist/assets/ -``` - -### Code Splitting - -The build automatically splits code for optimal loading: - -- Main application bundle -- Vendor dependencies -- Dynamic imports for large features - -### Compression - -Production builds include: - -- Gzip compression -- Minification -- Tree shaking -- Dead code elimination - -## Deployment Verification - -### Health Checks - -After deployment, verify: - -1. Application loads without errors -2. Database connection works -3. Authentication functions properly -4. Email notifications work (if configured) -5. OAuth login works (if configured) - -### Performance Checks - -1. Initial load time < 3 seconds -2. Bundle size reasonable (check warnings) -3. No console errors in browser -4. Proper caching headers set - -## Rollback Procedures - -### Quick Rollback - -```bash -# Rollback Kubernetes deployment -kubectl rollout undo deployment/rxminder -n rxminder-prod - -# Rollback Docker deployment -docker-compose -f docker/docker-compose.prod.yml down -# Deploy previous image version -``` - -### Configuration Rollback - -```bash -# Revert to previous configuration -git checkout HEAD~1 -- .env.production -make build-prod -make deploy-prod -``` - ---- - -## Related Documentation - -- [Deployment Guide](DEPLOYMENT.md) - General deployment instructions -- [Docker Configuration](DOCKER_IMAGE_CONFIGURATION.md) - Docker setup -- [Environment Variables](../ENVIRONMENT_VARIABLES.md) - All environment variables -- [Security](../development/SECURITY.md) - Security guidelines - ---- - -**Last Updated:** January 2024 -**Version:** 2.0.0 -**Status:** Production Ready diff --git a/docs/deployment/STORAGE_CONFIGURATION.md b/docs/deployment/STORAGE_CONFIGURATION.md deleted file mode 100644 index 1c326bc..0000000 --- a/docs/deployment/STORAGE_CONFIGURATION.md +++ /dev/null @@ -1,226 +0,0 @@ -# πŸ“¦ Storage Configuration Examples - -## Overview - -RxMinder now supports configurable storage through environment variables, making it easy to adapt to different Kubernetes environments and storage requirements. - -## πŸ—‚οΈ Storage Configuration Variables - -### **STORAGE_CLASS** - -The Kubernetes StorageClass to use for persistent volumes. - -**Common Options:** - -- `longhorn` - Longhorn distributed storage (Raspberry Pi clusters) -- `local-path` - Local path provisioner (k3s default) -- `standard` - Cloud provider standard storage -- `fast-ssd` - High-performance SSD storage -- `gp2` - AWS General Purpose SSD -- `pd-standard` - Google Cloud Standard Persistent Disk -- `azure-disk` - Azure Standard Disk - -### **STORAGE_SIZE** - -The amount of storage to allocate for the CouchDB database. - -**Sizing Guidelines:** - -- `1Gi` - Minimal testing (not recommended for production) -- `5Gi` - Small deployment (default, good for development) -- `10Gi` - Medium deployment (suitable for small teams) -- `20Gi` - Large deployment (production use) -- `50Gi+` - Enterprise deployment (high-volume usage) - -## 🎯 Environment-Specific Examples - -### Development (.env) - -```bash -# Development environment -APP_NAME=rxminder-dev -STORAGE_CLASS=local-path -STORAGE_SIZE=5Gi -INGRESS_HOST=rxminder-dev.local -``` - -### Staging (.env.staging) - -```bash -# Staging environment -APP_NAME=rxminder-staging -STORAGE_CLASS=longhorn -STORAGE_SIZE=10Gi -INGRESS_HOST=staging.rxminder.company.com -``` - -### Production (.env.production) - -```bash -# Production environment -APP_NAME=rxminder -STORAGE_CLASS=fast-ssd -STORAGE_SIZE=50Gi -INGRESS_HOST=rxminder.company.com -``` - -### Cloud Providers - -#### AWS EKS - -```bash -APP_NAME=rxminder -STORAGE_CLASS=gp2 # General Purpose SSD -STORAGE_SIZE=20Gi -INGRESS_HOST=rxminder.aws.company.com -``` - -#### Google GKE - -```bash -APP_NAME=rxminder -STORAGE_CLASS=pd-standard # Standard Persistent Disk -STORAGE_SIZE=20Gi -INGRESS_HOST=rxminder.gcp.company.com -``` - -#### Azure AKS - -```bash -APP_NAME=rxminder -STORAGE_CLASS=managed-premium # Premium SSD -STORAGE_SIZE=20Gi -INGRESS_HOST=rxminder.azure.company.com -``` - -## πŸ—οΈ Generated Kubernetes Resources - -### Before (Hardcoded) - -```yaml -# Old approach - hardcoded values -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: couchdb-pvc -spec: - storageClassName: longhorn - resources: - requests: - storage: 1Gi -``` - -### After (Template-Based) - -```yaml -# Template: k8s/couchdb-pvc.yaml.template -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ${APP_NAME}-couchdb-pvc - labels: - app: ${APP_NAME} -spec: - storageClassName: ${STORAGE_CLASS} - resources: - requests: - storage: ${STORAGE_SIZE} -``` - -### Deployed Result - -```yaml -# After envsubst processing -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: rxminder-couchdb-pvc - labels: - app: rxminder -spec: - storageClassName: fast-ssd - resources: - requests: - storage: 20Gi -``` - -## πŸš€ Deployment Examples - -### Quick Development Setup - -```bash -# Development with local storage -export APP_NAME=rxminder-dev -export STORAGE_CLASS=local-path -export STORAGE_SIZE=5Gi -./scripts/k8s-deploy-template.sh deploy -``` - -### Production Deployment - -```bash -# Copy production environment -cp .env.production .env -# Edit with your specific values -nano .env - -# Deploy to production -./scripts/k8s-deploy-template.sh deploy -``` - -### Custom Configuration - -```bash -# Override specific values -export STORAGE_CLASS=custom-storage -export STORAGE_SIZE=100Gi -./scripts/k8s-deploy-template.sh deploy -``` - -## πŸ” Storage Class Discovery - -### Find Available Storage Classes - -```bash -# List available storage classes in your cluster -kubectl get storageclass - -# Get details about a specific storage class -kubectl describe storageclass longhorn -``` - -### Common Storage Class Names by Platform - -| Platform | Common Storage Classes | -| -------------- | ------------------------------------------ | -| **k3s** | `local-path` (default) | -| **Longhorn** | `longhorn` | -| **AWS EKS** | `gp2`, `gp3`, `io1`, `io2` | -| **Google GKE** | `standard`, `ssd`, `pd-standard`, `pd-ssd` | -| **Azure AKS** | `default`, `managed-premium` | -| **Rancher** | `longhorn`, `local-path` | - -## πŸ’‘ Benefits - -### Flexibility - -- βœ… **Environment-specific** storage configuration -- βœ… **Cloud-agnostic** deployment -- βœ… **Performance tuning** via storage class selection -- βœ… **Cost optimization** through appropriate sizing - -### Maintainability - -- βœ… **Single source of truth** via `.env` files -- βœ… **Easy scaling** by changing STORAGE_SIZE -- βœ… **Environment promotion** using different .env files -- βœ… **Disaster recovery** with consistent configurations - -### Developer Experience - -- βœ… **No hardcoded values** in manifests -- βœ… **Clear documentation** of requirements -- βœ… **Validation** of required variables -- βœ… **Automated deployment** with proper storage setup - -This approach makes RxMinder truly **portable** across different Kubernetes environments while maintaining **production-grade** storage management! diff --git a/k8s-kustomize/README.md b/k8s-kustomize/README.md deleted file mode 100644 index d1b15cd..0000000 --- a/k8s-kustomize/README.md +++ /dev/null @@ -1,352 +0,0 @@ -# Kustomize Deployment Configuration - -This directory contains the Kustomize configuration for deploying the rxminder (Medication Reminder) application to Kubernetes. Kustomize provides a template-free way to customize application configuration that simplifies the use of off-the-shelf applications. - -## Directory Structure - -``` -k8s-kustomize/ -β”œβ”€β”€ base/ # Base configuration (shared resources) -β”‚ β”œβ”€β”€ kustomization.yaml # Base kustomization file -β”‚ β”œβ”€β”€ config.env # Environment variables for ConfigMap -β”‚ β”œβ”€β”€ registry-config.json # Docker registry configuration -β”‚ β”œβ”€β”€ frontend-deployment.yaml # Frontend deployment -β”‚ β”œβ”€β”€ frontend-service.yaml # Frontend service -β”‚ β”œβ”€β”€ couchdb-statefulset.yaml # CouchDB database -β”‚ β”œβ”€β”€ couchdb-service.yaml # CouchDB service -β”‚ β”œβ”€β”€ couchdb-pvc.yaml # CouchDB persistent volume -β”‚ β”œβ”€β”€ configmap.yaml # Application configuration -β”‚ β”œβ”€β”€ ingress.yaml # Ingress configuration -β”‚ β”œβ”€β”€ network-policy.yaml # Network policies -β”‚ β”œβ”€β”€ hpa.yaml # Horizontal Pod Autoscaler -β”‚ └── db-seed-job.yaml # Database seeding job -β”œβ”€β”€ overlays/ # Environment-specific overrides -β”‚ β”œβ”€β”€ dev/ # Development environment -β”‚ β”‚ └── kustomization.yaml # Development customizations -β”‚ └── prod/ # Production environment -β”‚ β”œβ”€β”€ kustomization.yaml # Production customizations -β”‚ β”œβ”€β”€ frontend-resources.yaml # Production frontend resources -β”‚ β”œβ”€β”€ couchdb-resources.yaml # Production database resources -β”‚ └── ingress-prod.yaml # Production ingress config -└── README.md # This file -``` - -## Quick Start - -### Prerequisites - -1. **kubectl** installed and configured -2. **Kubernetes cluster** access -3. **Docker images** built and pushed to registry - -### Deploy to Development - -```bash -# Using Makefile (recommended) -make deploy-dev - -# Or directly with kubectl -kubectl apply -k k8s-kustomize/overlays/dev -``` - -### Deploy to Production - -```bash -# Using Makefile (recommended) -make deploy-prod - -# Or directly with kubectl -kubectl apply -k k8s-kustomize/overlays/prod -``` - -## Environment Configurations - -### Development Environment - -- **Namespace**: `rxminder-dev` -- **Replicas**: 1 frontend pod -- **Resources**: Minimal (16Mi-32Mi memory) -- **Image Tag**: `dev` -- **Domain**: `rxminder-dev.local` -- **Storage**: 1Gi -- **Security**: Relaxed for development - -### Production Environment - -- **Namespace**: `rxminder-prod` -- **Replicas**: 3 frontend pods (high availability) -- **Resources**: Production-grade (256Mi-512Mi memory) -- **Image Tag**: `v1.0.0` (semantic versioning) -- **Domain**: `rxminder.yourdomain.com` -- **Storage**: 10Gi SSD -- **Security**: Hardened with security contexts, network policies -- **TLS**: Enabled with cert-manager -- **Monitoring**: Enabled - -## Makefile Commands - -### Deployment Commands - -```bash -# Development -make deploy-dev # Deploy to development -make undeploy-dev # Remove development deployment -make quick-deploy-dev # Build and deploy to development -make status-dev # Show development status - -# Production -make deploy-prod # Deploy to production -make undeploy-prod # Remove production deployment -make quick-deploy-prod # Build and deploy to production -make status-prod # Show production status -``` - -### Validation Commands - -```bash -make validate-kustomize # Validate all configurations -make kustomize-validate-dev # Validate development config -make kustomize-validate-prod # Validate production config -``` - -### Debugging Commands - -```bash -make kustomize-dry-run-dev # Dry run development deployment -make kustomize-dry-run-prod # Dry run production deployment -make kustomize-diff-dev # Show differences for development -make kustomize-diff-prod # Show differences for production -make kustomize-build-dev # Build development manifests -make kustomize-build-prod # Build production manifests -``` - -## Configuration Management - -### ConfigMaps - -Configuration is managed through: - -1. **Base config.env**: Common environment variables -2. **Overlay literals**: Environment-specific overrides -3. **ConfigMap generation**: Automatic from environment files - -### Secrets - -Secrets are managed through: - -1. **Development**: Simple literals in kustomization.yaml -2. **Production**: External secret management (recommended) - - Kubernetes External Secrets Operator - - HashiCorp Vault - - AWS Secrets Manager - - Azure Key Vault - -### Images - -Image management uses Kustomize's image transformer: - -```yaml -images: - - name: gitea-http.taildb3494.ts.net/will/rxminder - newTag: v1.0.0 # Override in overlays -``` - -## Customization - -### Adding a New Environment - -1. Create new directory: `overlays/staging/` -2. Create `kustomization.yaml` with base reference -3. Add environment-specific patches -4. Update Makefile with new targets - -Example: - -```yaml -# overlays/staging/kustomization.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - ../../base - -namespace: rxminder-staging - -commonLabels: - environment: staging - -images: - - name: gitea-http.taildb3494.ts.net/will/rxminder - newTag: staging -``` - -### Resource Patches - -Use strategic merge patches for complex modifications: - -```yaml -# Example: frontend-patch.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: rxminder-frontend -spec: - template: - spec: - containers: - - name: frontend - resources: - requests: - memory: '512Mi' - cpu: '200m' -``` - -### JSON Patches - -Use JSON patches for precise modifications: - -```yaml -patches: - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/replicas - value: 5 -``` - -## Security Considerations - -### Production Security Features - -1. **Security Contexts**: Non-root containers, read-only filesystems -2. **Network Policies**: Restricted pod-to-pod communication -3. **Resource Limits**: Prevent resource exhaustion -4. **Image Security**: Signed images, vulnerability scanning -5. **Secret Management**: External secret stores -6. **TLS Encryption**: HTTPS with cert-manager -7. **RBAC**: Role-based access control - -### Development Security - -- Relaxed for development efficiency -- Still follows basic security practices -- Isolated in separate namespace - -## Monitoring and Observability - -### Production Monitoring - -- **Health Checks**: Liveness and readiness probes -- **Metrics**: Resource usage monitoring -- **Logging**: Structured logging with appropriate levels -- **Alerting**: Production-grade alert rules - -### Development Monitoring - -- **Debug Logging**: Verbose logging for troubleshooting -- **Resource Monitoring**: Basic resource tracking -- **Health Checks**: Relaxed timing for development - -## Troubleshooting - -### Common Issues - -1. **Image Pull Errors** - - ```bash - # Check image exists and credentials are correct - kubectl describe pod -n - ``` - -2. **ConfigMap Issues** - - ```bash - # Check generated ConfigMap - kubectl get configmap rxminder-config -n -o yaml - ``` - -3. **Service Discovery** - - ```bash - # Test service connectivity - kubectl exec -it -n -- curl rxminder-couchdb-service:5984 - ``` - -4. **Resource Constraints** - - ```bash - # Check resource usage - kubectl top pods -n - kubectl describe node - ``` - -### Debug Commands - -```bash -# View all resources -kubectl get all -n rxminder-dev -l app=rxminder - -# Check events -kubectl get events -n rxminder-dev --sort-by='.lastTimestamp' - -# View logs -kubectl logs deployment/rxminder-frontend -n rxminder-dev -f - -# Describe problematic resources -kubectl describe deployment rxminder-frontend -n rxminder-dev -``` - -## Migration from Legacy Deployment - -### Migration Steps - -1. **Test Kustomize**: Deploy to development first -2. **Validate Configuration**: Compare with existing deployment -3. **Update CI/CD**: Switch to Kustomize commands -4. **Production Migration**: Schedule maintenance window -5. **Cleanup**: Remove old template files after validation - -### Rollback Plan - -Legacy deployment scripts are still available: - -```bash -make deploy-dev # Deploy to development environment -make deploy-prod # Deploy to production environment -make undeploy-dev # Remove development deployment -make undeploy-prod # Remove production deployment -``` - -## Benefits of Kustomize - -1. **Template-free**: No complex templating logic -2. **Composable**: Layer configurations naturally -3. **Validation**: Built-in YAML validation -4. **GitOps Ready**: Works with ArgoCD, Flux -5. **Standard**: Kubernetes-native solution -6. **Maintainable**: Clear separation of concerns - -## Best Practices - -1. **Base Configuration**: Keep base as generic as possible -2. **Environment Isolation**: Use separate namespaces -3. **Secret Management**: Use external secret stores in production -4. **Image Tags**: Use specific tags, avoid `latest` in production -5. **Resource Limits**: Always set resource requests and limits -6. **Health Checks**: Configure appropriate probes -7. **Documentation**: Keep this README updated - -## Support - -For issues or questions: - -1. Check troubleshooting section above -2. Review Kubernetes events and logs -3. Validate Kustomize configuration -4. Consult team documentation - ---- - -**Note**: This Kustomize configuration replaces the previous shell script-based deployment. The old scripts are still available for backward compatibility but Kustomize is the recommended approach going forward. diff --git a/k8s-kustomize/base/config.env b/k8s-kustomize/base/config.env deleted file mode 100644 index aaffe51..0000000 --- a/k8s-kustomize/base/config.env +++ /dev/null @@ -1,60 +0,0 @@ -# Base configuration for rxminder application -# Generated automatically from environment variables -# Generated on: Sun Sep 7 08:46:59 PM PDT 2025 -# Environment: dev - -# Application Environment -NODE_ENV=production -LOG_LEVEL=info - -# API Configuration -REACT_APP_API_URL=http://rxminder-couchdb-service:5984 - -# Feature Flags -ENABLE_MONITORING=false -DEBUG=false - -# Cache Configuration -CACHE_TTL=1800 - -# Database Configuration -DB_HOST=rxminder-couchdb-service -DB_PORT=5984 -COUCHDB_DATABASE_NAME=meds_app - -# Security Configuration -ENABLE_CORS=true -CORS_ORIGIN=* - -# Performance Configuration -REQUEST_TIMEOUT=30000 -MAX_CONNECTIONS=100 - -# Logging Configuration -LOG_FORMAT=json -LOG_TIMESTAMP=true - -# Health Check Configuration -HEALTH_CHECK_INTERVAL=30 -READINESS_CHECK_TIMEOUT=5 - -# Application Metadata -APP_NAME=rxminder -APP_VERSION=1.0.0 - -# Container Registry -REGISTRY_URL=gitea-http.taildb3494.ts.net -IMAGE_REPOSITORY=will/rxminder - -# Ingress Configuration -INGRESS_CLASS=nginx -CERT_MANAGER_ISSUER=letsencrypt-prod - -# Monitoring and Observability -ENABLE_METRICS=false -METRICS_PORT=9090 -ENABLE_TRACING=false - -# Development specific (will be overridden in overlays) -DEV_MODE=false -HOT_RELOAD=false diff --git a/k8s-kustomize/base/couchdb-pvc.yaml b/k8s-kustomize/base/couchdb-pvc.yaml deleted file mode 100644 index 3506405..0000000 --- a/k8s-kustomize/base/couchdb-pvc.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: rxminder-couchdb-pvc - labels: - app: rxminder - component: database -spec: - accessModes: - - ReadWriteOnce - storageClassName: standard - resources: - requests: - storage: 1Gi diff --git a/k8s-kustomize/base/couchdb-service.yaml b/k8s-kustomize/base/couchdb-service.yaml deleted file mode 100644 index 56dbdb6..0000000 --- a/k8s-kustomize/base/couchdb-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: rxminder-couchdb-service - labels: - app: rxminder - component: database -spec: - selector: - app: rxminder - component: database - ports: - - name: couchdb - port: 5984 - targetPort: 5984 - protocol: TCP - type: ClusterIP diff --git a/k8s-kustomize/base/couchdb-statefulset.yaml b/k8s-kustomize/base/couchdb-statefulset.yaml deleted file mode 100644 index 1321a2f..0000000 --- a/k8s-kustomize/base/couchdb-statefulset.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: rxminder-couchdb - labels: - app: rxminder - component: database -spec: - serviceName: rxminder-couchdb-service - replicas: 1 - selector: - matchLabels: - app: rxminder - component: database - template: - metadata: - labels: - app: rxminder - component: database - spec: - containers: - - name: couchdb - image: couchdb:3.3.2 - ports: - - containerPort: 5984 - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - resources: - requests: - memory: '64Mi' - cpu: '30m' - limits: - memory: '128Mi' - cpu: '60m' - volumeMounts: - - name: couchdb-data - mountPath: /opt/couchdb/data - livenessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 60 - periodSeconds: 30 - readinessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 10 - periodSeconds: 5 - volumeClaimTemplates: - - metadata: - name: couchdb-data - labels: - app: rxminder - component: database - spec: - accessModes: ['ReadWriteOnce'] - storageClassName: standard - resources: - requests: - storage: 1Gi diff --git a/k8s-kustomize/base/db-seed-job.yaml b/k8s-kustomize/base/db-seed-job.yaml deleted file mode 100644 index f5f4085..0000000 --- a/k8s-kustomize/base/db-seed-job.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: rxminder-db-seed - labels: - app: rxminder - component: database -spec: - template: - metadata: - labels: - app: rxminder - component: database - spec: - containers: - - name: db-seeder - image: couchdb:3.3.2 - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - command: ['/bin/sh', '-c'] - 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" - restartPolicy: Never - backoffLimit: 4 diff --git a/k8s-kustomize/base/frontend-deployment.yaml b/k8s-kustomize/base/frontend-deployment.yaml deleted file mode 100644 index 9de901e..0000000 --- a/k8s-kustomize/base/frontend-deployment.yaml +++ /dev/null @@ -1,45 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: rxminder-frontend - labels: - component: frontend -spec: - replicas: 1 - selector: - matchLabels: - component: frontend - template: - metadata: - labels: - component: frontend - spec: - imagePullSecrets: - - name: rxminder-registry-secret - containers: - - name: frontend - image: gitea-http.taildb3494.ts.net/will/rxminder:latest - ports: - - containerPort: 80 - envFrom: - - configMapRef: - name: rxminder-config - resources: - requests: - memory: '32Mi' - cpu: '20m' - limits: - memory: '64Mi' - cpu: '40m' - livenessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 30 - periodSeconds: 30 - readinessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 5 - periodSeconds: 5 diff --git a/k8s-kustomize/base/frontend-service.yaml b/k8s-kustomize/base/frontend-service.yaml deleted file mode 100644 index a0545dd..0000000 --- a/k8s-kustomize/base/frontend-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: rxminder-frontend-service - labels: - app: rxminder - component: frontend -spec: - selector: - app: rxminder - component: frontend - ports: - - name: http - port: 80 - targetPort: 80 - protocol: TCP - type: ClusterIP diff --git a/k8s-kustomize/base/hpa.yaml b/k8s-kustomize/base/hpa.yaml deleted file mode 100644 index 22cc7be..0000000 --- a/k8s-kustomize/base/hpa.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: rxminder-frontend-hpa - labels: - app: rxminder - component: frontend -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: rxminder-frontend - minReplicas: 1 - maxReplicas: 3 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 50 diff --git a/k8s-kustomize/base/ingress.yaml b/k8s-kustomize/base/ingress.yaml deleted file mode 100644 index 268990d..0000000 --- a/k8s-kustomize/base/ingress.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: rxminder-ingress - labels: - app: rxminder - component: frontend - annotations: - # Add SSL redirect if using HTTPS - # nginx.ingress.kubernetes.io/ssl-redirect: "true" - # cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - ingressClassName: nginx - # Uncomment for HTTPS with cert-manager - # tls: - # - hosts: - # - rxminder.example.com - # secretName: frontend-tls - rules: - - host: rxminder.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: rxminder-frontend-service - port: - number: 80 diff --git a/k8s-kustomize/base/kustomization.yaml b/k8s-kustomize/base/kustomization.yaml deleted file mode 100644 index 1c34b46..0000000 --- a/k8s-kustomize/base/kustomization.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-base - -namespace: rxminder - -resources: - - frontend-deployment.yaml - - frontend-service.yaml - - couchdb-statefulset.yaml - - couchdb-service.yaml - - couchdb-pvc.yaml - - ingress.yaml - - network-policy.yaml - - hpa.yaml - - db-seed-job.yaml - -# Common labels applied to all resources -labels: - - pairs: - app: rxminder - version: v1.0.0 - -# Generate ConfigMap from environment files -configMapGenerator: - - name: rxminder-config - envs: - - config.env - behavior: create - -# Generate Secret for CouchDB -secretGenerator: - - name: couchdb-secret - literals: - - username=admin - - password=changeme - type: Opaque - - # Note: Registry secret should be created manually or via external secret management - # For now, this is commented out to avoid issues with missing files - # - name: rxminder-registry-secret - # files: - # - .dockerconfigjson=registry-config.json - # type: kubernetes.io/dockerconfigjson - -# Images to be used (can be overridden in overlays) -images: - - name: gitea-http.taildb3494.ts.net/will/rxminder - newName: gitea-http.taildb3494.ts.net/will/rxminder - newTag: latest - - name: couchdb - newName: couchdb - newTag: 3.3.2 - -# Replicas (can be overridden in overlays) -replicas: - - name: rxminder-frontend - count: 1 - - name: rxminder-couchdb - count: 1 diff --git a/k8s-kustomize/base/network-policy.yaml b/k8s-kustomize/base/network-policy.yaml deleted file mode 100644 index e2ce07a..0000000 --- a/k8s-kustomize/base/network-policy.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: rxminder-frontend-policy - labels: - app: rxminder - component: frontend -spec: - podSelector: - matchLabels: - component: frontend - policyTypes: - - Ingress - - Egress - ingress: - - from: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 80 - egress: - - to: - - podSelector: - matchLabels: - component: database - ports: - - protocol: TCP - port: 5984 - - to: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 80 ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: rxminder-database-policy - labels: - app: rxminder - component: database -spec: - podSelector: - matchLabels: - component: database - policyTypes: - - Ingress - - Egress - ingress: - - from: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 5984 - egress: - - to: - - podSelector: - matchLabels: - component: database - ports: - - protocol: TCP - port: 5984 diff --git a/k8s-kustomize/base/registry-config.json b/k8s-kustomize/base/registry-config.json deleted file mode 100644 index 8f2e996..0000000 --- a/k8s-kustomize/base/registry-config.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "auths": { - "gitea-http.taildb3494.ts.net": { - "username": "REGISTRY_USERNAME", - "password": "REGISTRY_PASSWORD", - "email": "REGISTRY_EMAIL", - "auth": "REGISTRY_AUTH_TOKEN" - }, - "docker.io": { - "username": "DOCKER_USERNAME", - "password": "DOCKER_PASSWORD", - "email": "DOCKER_EMAIL", - "auth": "DOCKER_AUTH_TOKEN" - } - }, - "HttpHeaders": { - "User-Agent": "Docker-Client/20.10.0 (linux)" - }, - "credsStore": "secretservice", - "experimental": "disabled" -} diff --git a/k8s-kustomize/overlays/dev/config.env b/k8s-kustomize/overlays/dev/config.env deleted file mode 100644 index 40251a8..0000000 --- a/k8s-kustomize/overlays/dev/config.env +++ /dev/null @@ -1,23 +0,0 @@ -# Development environment configuration -# Generated on: Sun Sep 7 08:46:59 PM PDT 2025 - -NODE_ENV=development -LOG_LEVEL=debug -DEBUG=true -ENABLE_MONITORING=false -DEV_MODE=true -HOT_RELOAD=true - -# Development URLs (override if needed) -REACT_APP_API_URL=http://rxminder-couchdb-service:5984 -CORS_ORIGIN=* - -# Development domain -INGRESS_HOST=rxminder-dev.local - -# Relaxed timeouts for debugging -REQUEST_TIMEOUT=60000 -HEALTH_CHECK_INTERVAL=60 - -# Development image tag -IMAGE_TAG=dev diff --git a/k8s-kustomize/overlays/dev/kustomization.yaml b/k8s-kustomize/overlays/dev/kustomization.yaml deleted file mode 100644 index a598920..0000000 --- a/k8s-kustomize/overlays/dev/kustomization.yaml +++ /dev/null @@ -1,92 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-dev - -namespace: rxminder-dev - -resources: - - ../../base - -# Development-specific labels -labels: - - pairs: - environment: dev - -# Override images for development -images: - - name: gitea-http.taildb3494.ts.net/will/rxminder - newName: gitea-http.taildb3494.ts.net/will/rxminder - newTag: dev - - name: couchdb - newName: couchdb - newTag: 3.3.2 - -# Development replicas (lower for resource conservation) -replicas: - - name: rxminder-frontend - count: 1 - -# Development-specific patches -patches: - # Development environment variables via ConfigMap - - target: - kind: ConfigMap - name: rxminder-config - patch: |- - - op: replace - path: /data/NODE_ENV - value: "development" - - op: replace - path: /data/LOG_LEVEL - value: "debug" - - op: add - path: /data/DEBUG - value: "true" - - # Development resource limits - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources/requests/memory - value: "16Mi" - - op: replace - path: /spec/template/spec/containers/0/resources/limits/memory - value: "32Mi" - - op: add - path: /spec/template/spec/containers/0/env - value: - - name: NODE_ENV - value: "development" - - name: LOG_LEVEL - value: "debug" - - # Development ingress host - - target: - kind: Ingress - name: rxminder-ingress - patch: |- - - op: replace - path: /spec/rules/0/host - value: "rxminder-dev.local" - - # Development storage size - - target: - kind: PersistentVolumeClaim - name: rxminder-couchdb-pvc - patch: |- - - op: replace - path: /spec/resources/requests/storage - value: "1Gi" - -# Development secrets (use weak passwords for dev) -secretGenerator: - - name: couchdb-secret - literals: - - username=admin - - password=devpass123 - type: Opaque - behavior: replace diff --git a/k8s-kustomize/overlays/development/config.env b/k8s-kustomize/overlays/development/config.env deleted file mode 100644 index 51ddac2..0000000 --- a/k8s-kustomize/overlays/development/config.env +++ /dev/null @@ -1,43 +0,0 @@ -# Kubernetes configuration for development -# Generated automatically from unified configuration -# Generated on: 2025-09-09T03:27:30.598Z - -APP_NAME=rxminder -APP_VERSION=1.0.0 -NODE_ENV=development -APP_BASE_URL=rxminder.192.168.153.243.nip.io -PORT=5173 -COUCHDB_URL=http://rxminder-couchdb-service:5984 -COUCHDB_USER=admin -COUCHDB_PASSWORD=L7tfqHyg0T4sIYiWK -COUCHDB_DATABASE_NAME=meds_app -USE_MOCK_DB=false -KUBERNETES_NAMESPACE=rxminder-dev -INGRESS_HOST=rxminder.192.168.153.243.nip.io -INGRESS_CLASS=nginx -CERT_MANAGER_ISSUER=selfsigned -STORAGE_CLASS=longhorn -STORAGE_SIZE=1Gi -ENABLE_EMAIL_VERIFICATION=true -ENABLE_OAUTH=true -ENABLE_ADMIN_INTERFACE=true -ENABLE_MONITORING=false -ENABLE_METRICS=false -DEBUG_MODE=true -LOG_LEVEL=debug -LOG_FORMAT=text -CACHE_TTL=300 -REQUEST_TIMEOUT=30000 -MAX_CONNECTIONS=100 -ENABLE_CORS=true -CORS_ORIGIN=* -FRONTEND_REPLICAS=1 -DATABASE_REPLICAS=1 -FRONTEND_MEMORY_REQUEST=128Mi -FRONTEND_CPU_REQUEST=50m -FRONTEND_MEMORY_LIMIT=256Mi -FRONTEND_CPU_LIMIT=200m -DATABASE_MEMORY_REQUEST=256Mi -DATABASE_CPU_REQUEST=100m -DATABASE_MEMORY_LIMIT=512Mi -DATABASE_CPU_LIMIT=500m diff --git a/k8s-kustomize/overlays/development/kustomization.yaml b/k8s-kustomize/overlays/development/kustomization.yaml deleted file mode 100644 index 28993b1..0000000 --- a/k8s-kustomize/overlays/development/kustomization.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-development - -# Reference the base configuration -resources: - - ../../base - - namespace.yaml - -# Override namespace for development -namespace: rxminder-dev - -# Development-specific labels -labels: - - pairs: - environment: development - tier: development - -# Development image tags and configurations -images: - - name: frontend-image - newName: gitea-http.taildb3494.ts.net/will/meds - newTag: latest - - name: couchdb-image - newName: couchdb - newTag: 3.3.2 - -# Development replicas -replicas: - - name: rxminder-frontend - count: 1 - - name: rxminder-couchdb - count: 1 - -# Environment-specific patches -patches: - # Resource limits - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "128Mi" - cpu: "50m" - limits: - memory: "256Mi" - cpu: "200m" - - - target: - kind: StatefulSet - name: rxminder-couchdb - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - -# ConfigMap generation -configMapGenerator: - - name: rxminder-config - envs: - - config.env - behavior: create diff --git a/k8s-kustomize/overlays/prod/couchdb-resources.yaml b/k8s-kustomize/overlays/prod/couchdb-resources.yaml deleted file mode 100644 index 71dd5bd..0000000 --- a/k8s-kustomize/overlays/prod/couchdb-resources.yaml +++ /dev/null @@ -1,126 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: rxminder-couchdb -spec: - template: - spec: - containers: - - name: couchdb - resources: - requests: - memory: '512Mi' - cpu: '200m' - limits: - memory: '1Gi' - cpu: '1000m' - # Production environment variables - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - # Production CouchDB configuration - - name: ERL_FLAGS - value: '-setcookie monster' - - name: COUCHDB_SECRET - value: 'changeme_in_production' - # Production health checks with tighter timings - livenessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 60 - periodSeconds: 20 - timeoutSeconds: 10 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 5 - failureThreshold: 3 - # Security context for production - securityContext: - allowPrivilegeEscalation: false - runAsNonRoot: true - runAsUser: 5984 - runAsGroup: 5984 - capabilities: - drop: - - ALL - # Volume mounts with proper permissions - volumeMounts: - - name: couchdb-data - mountPath: /opt/couchdb/data - - name: couchdb-config - mountPath: /opt/couchdb/etc/local.d - readOnly: true - # Pod-level security and scheduling for production - securityContext: - fsGroup: 5984 - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - # Production scheduling preferences for database - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - rxminder - - key: component - operator: In - values: - - database - topologyKey: kubernetes.io/hostname - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - preference: - matchExpressions: - - key: node-type - operator: In - values: - - database - - storage - # Toleration for production node taints - tolerations: - - key: 'node-role.kubernetes.io/production' - operator: 'Equal' - value: 'true' - effect: 'NoSchedule' - - key: 'node-role.kubernetes.io/database' - operator: 'Equal' - value: 'true' - effect: 'NoSchedule' - # Additional volumes for production configuration - volumes: - - name: couchdb-config - configMap: - name: couchdb-production-config - # Production volume claim template with SSD storage - volumeClaimTemplates: - - metadata: - name: couchdb-data - labels: - app: rxminder - component: database - environment: production - spec: - accessModes: ['ReadWriteOnce'] - storageClassName: ssd - resources: - requests: - storage: 10Gi diff --git a/k8s-kustomize/overlays/prod/frontend-resources.yaml b/k8s-kustomize/overlays/prod/frontend-resources.yaml deleted file mode 100644 index 2768e4a..0000000 --- a/k8s-kustomize/overlays/prod/frontend-resources.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: rxminder-frontend -spec: - template: - spec: - containers: - - name: frontend - resources: - requests: - memory: '256Mi' - cpu: '100m' - limits: - memory: '512Mi' - cpu: '500m' - # Production environment variables - env: - - name: NODE_ENV - value: 'production' - - name: LOG_LEVEL - value: 'warn' - - name: ENABLE_MONITORING - value: 'true' - # Production readiness and liveness probes with tighter timings - livenessProbe: - httpGet: - path: /health - port: 80 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /ready - port: 80 - initialDelaySeconds: 5 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 3 - # Security context for production - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - capabilities: - drop: - - ALL - # Pod-level security and scheduling for production - securityContext: - fsGroup: 1000 - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - # Production scheduling preferences - affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - rxminder - - key: component - operator: In - values: - - frontend - topologyKey: kubernetes.io/hostname - # Toleration for production node taints - tolerations: - - key: 'node-role.kubernetes.io/production' - operator: 'Equal' - value: 'true' - effect: 'NoSchedule' diff --git a/k8s-kustomize/overlays/prod/ingress-prod.yaml b/k8s-kustomize/overlays/prod/ingress-prod.yaml deleted file mode 100644 index 5efa8a1..0000000 --- a/k8s-kustomize/overlays/prod/ingress-prod.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: rxminder-ingress - annotations: - # Enable SSL redirect for production - nginx.ingress.kubernetes.io/ssl-redirect: 'true' - # Use production certificate issuer - cert-manager.io/cluster-issuer: 'letsencrypt-prod' - # Security headers for production - nginx.ingress.kubernetes.io/configuration-snippet: | - more_set_headers "X-Frame-Options: DENY"; - more_set_headers "X-Content-Type-Options: nosniff"; - more_set_headers "X-XSS-Protection: 1; mode=block"; - more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload"; - more_set_headers "Referrer-Policy: strict-origin-when-cross-origin"; - # Rate limiting for production - nginx.ingress.kubernetes.io/rate-limit: '100' - nginx.ingress.kubernetes.io/rate-limit-window: '1m' - # Enable CORS for production API access - nginx.ingress.kubernetes.io/enable-cors: 'true' - nginx.ingress.kubernetes.io/cors-allow-origin: 'https://rxminder.yourdomain.com' - # Compression for better performance - nginx.ingress.kubernetes.io/compression: 'gzip' - # Client body size limit - nginx.ingress.kubernetes.io/proxy-body-size: '10m' - # Connection and read timeouts - nginx.ingress.kubernetes.io/proxy-connect-timeout: '60' - nginx.ingress.kubernetes.io/proxy-read-timeout: '60' - # Enable modsecurity WAF for production - nginx.ingress.kubernetes.io/enable-modsecurity: 'true' - nginx.ingress.kubernetes.io/modsecurity-snippet: | - SecRuleEngine On - SecAuditEngine RelevantOnly -spec: - ingressClassName: nginx - # Production TLS configuration - tls: - - hosts: - - rxminder.yourdomain.com - - api.rxminder.yourdomain.com - secretName: rxminder-tls-prod - rules: - # Main application domain - - host: rxminder.yourdomain.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: rxminder-frontend-service - port: - number: 80 - # API subdomain for direct database access (if needed) - - host: api.rxminder.yourdomain.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: rxminder-couchdb-service - port: - number: 5984 diff --git a/k8s-kustomize/overlays/prod/kustomization.yaml b/k8s-kustomize/overlays/prod/kustomization.yaml deleted file mode 100644 index 7498e7b..0000000 --- a/k8s-kustomize/overlays/prod/kustomization.yaml +++ /dev/null @@ -1,128 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-production - -# Reference the base configuration -resources: - - ../../base - - namespace.yaml - -# Override namespace for production -namespace: rxminder-prod - -# Production-specific labels -labels: - - pairs: - environment: production - tier: prod - -# Production image tags and configurations -images: - - name: frontend-image - newName: gitea-http.taildb3494.ts.net/will/rxminder - newTag: v1.0.0 - - name: couchdb-image - newName: couchdb - newTag: 3.3.2 - -# Production replicas - higher for availability -replicas: - - name: rxminder-frontend - count: 3 - - name: rxminder-couchdb - count: 1 - -# Production resource patches -patchesStrategicMerge: - - frontend-resources.yaml - - couchdb-resources.yaml - - ingress-prod.yaml - -# Production secrets (to be created manually or via external secret management) -secretGenerator: - - name: couchdb-secret - behavior: replace - literals: - - username=admin - # Note: In production, use external secret management like: - # - Kubernetes External Secrets Operator - # - HashiCorp Vault - # - AWS Secrets Manager - # This is just a placeholder - - password=CHANGE_IN_PRODUCTION - -# Production-specific patches for security and performance -patches: - # Production-specific ConfigMap patches - - target: - kind: ConfigMap - name: rxminder-config - patch: |- - - op: replace - path: /data/NODE_ENV - value: "production" - - op: replace - path: /data/LOG_LEVEL - value: "warn" - - op: add - path: /data/ENABLE_MONITORING - value: "true" - - op: add - path: /data/CACHE_TTL - value: "3600" - - # Enable HTTPS redirect on ingress - - target: - kind: Ingress - name: rxminder-ingress - patch: |- - - op: add - path: /metadata/annotations/nginx.ingress.kubernetes.io~1ssl-redirect - value: "true" - - op: add - path: /metadata/annotations/cert-manager.io~1cluster-issuer - value: "letsencrypt-prod" - - # Add resource limits for production workloads - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - - # Production storage class and size - - target: - kind: StatefulSet - name: rxminder-couchdb - patch: |- - - op: replace - path: /spec/volumeClaimTemplates/0/spec/storageClassName - value: "ssd" - - op: replace - path: /spec/volumeClaimTemplates/0/spec/resources/requests/storage - value: "10Gi" - - # Production CouchDB resources - - target: - kind: StatefulSet - name: rxminder-couchdb - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "512Mi" - cpu: "200m" - limits: - memory: "1Gi" - cpu: "1000m" diff --git a/k8s-kustomize/overlays/prod/namespace.yaml b/k8s-kustomize/overlays/prod/namespace.yaml deleted file mode 100644 index 355be94..0000000 --- a/k8s-kustomize/overlays/prod/namespace.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: rxminder-prod - labels: - app: rxminder - environment: production - tier: prod - version: v1.0.0 diff --git a/k8s-kustomize/overlays/production/config.env b/k8s-kustomize/overlays/production/config.env deleted file mode 100644 index 5537fc1..0000000 --- a/k8s-kustomize/overlays/production/config.env +++ /dev/null @@ -1,43 +0,0 @@ -# Kubernetes configuration for production -# Generated automatically from unified configuration -# Generated on: 2025-09-09T03:27:30.604Z - -APP_NAME=rxminder -APP_VERSION=1.0.0 -NODE_ENV=production -APP_BASE_URL=rxminder.192.168.153.243.nip.io -PORT=5173 -COUCHDB_URL=http://rxminder-couchdb-service:5984 -COUCHDB_USER=admin -COUCHDB_PASSWORD=L7tfqHyg0T4sIYiWK -COUCHDB_DATABASE_NAME=meds_app -USE_MOCK_DB=false -KUBERNETES_NAMESPACE=rxminder-prod -INGRESS_HOST=rxminder.192.168.153.243.nip.io -INGRESS_CLASS=nginx -CERT_MANAGER_ISSUER=letsencrypt-prod -STORAGE_CLASS=longhorn -STORAGE_SIZE=1Gi -ENABLE_EMAIL_VERIFICATION=true -ENABLE_OAUTH=true -ENABLE_ADMIN_INTERFACE=true -ENABLE_MONITORING=false -ENABLE_METRICS=false -DEBUG_MODE=true -LOG_LEVEL=debug -LOG_FORMAT=json -CACHE_TTL=3600 -REQUEST_TIMEOUT=120000 -MAX_CONNECTIONS=200 -ENABLE_CORS=true -CORS_ORIGIN=https://rxminder.com -FRONTEND_REPLICAS=3 -DATABASE_REPLICAS=1 -FRONTEND_MEMORY_REQUEST=256Mi -FRONTEND_CPU_REQUEST=100m -FRONTEND_MEMORY_LIMIT=512Mi -FRONTEND_CPU_LIMIT=500m -DATABASE_MEMORY_REQUEST=512Mi -DATABASE_CPU_REQUEST=200m -DATABASE_MEMORY_LIMIT=1Gi -DATABASE_CPU_LIMIT=1000m diff --git a/k8s-kustomize/overlays/production/kustomization.yaml b/k8s-kustomize/overlays/production/kustomization.yaml deleted file mode 100644 index 8413772..0000000 --- a/k8s-kustomize/overlays/production/kustomization.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-production - -# Reference the base configuration -resources: - - ../../base - - namespace.yaml - -# Override namespace for production -namespace: rxminder-prod - -# Production-specific labels -labels: - - pairs: - environment: production - tier: prod - -# Production image tags and configurations -images: - - name: frontend-image - newName: gitea-http.taildb3494.ts.net/will/meds - newTag: latest - - name: couchdb-image - newName: couchdb - newTag: 3.3.2 - -# Production replicas -replicas: - - name: rxminder-frontend - count: 3 - - name: rxminder-couchdb - count: 1 - -# Environment-specific patches -patches: - # Resource limits - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - - - target: - kind: StatefulSet - name: rxminder-couchdb - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "512Mi" - cpu: "200m" - limits: - memory: "1Gi" - cpu: "1000m" - -# ConfigMap generation -configMapGenerator: - - name: rxminder-config - envs: - - config.env - behavior: create diff --git a/k8s-kustomize/overlays/staging/config.env b/k8s-kustomize/overlays/staging/config.env deleted file mode 100644 index b6a1a97..0000000 --- a/k8s-kustomize/overlays/staging/config.env +++ /dev/null @@ -1,43 +0,0 @@ -# Kubernetes configuration for staging -# Generated automatically from unified configuration -# Generated on: 2025-09-09T03:27:30.602Z - -APP_NAME=rxminder -APP_VERSION=1.0.0 -NODE_ENV=staging -APP_BASE_URL=rxminder.192.168.153.243.nip.io -PORT=5173 -COUCHDB_URL=http://rxminder-couchdb-service:5984 -COUCHDB_USER=admin -COUCHDB_PASSWORD=L7tfqHyg0T4sIYiWK -COUCHDB_DATABASE_NAME=meds_app -USE_MOCK_DB=false -KUBERNETES_NAMESPACE=rxminder-staging -INGRESS_HOST=rxminder.192.168.153.243.nip.io -INGRESS_CLASS=nginx -CERT_MANAGER_ISSUER=letsencrypt-staging -STORAGE_CLASS=longhorn -STORAGE_SIZE=1Gi -ENABLE_EMAIL_VERIFICATION=true -ENABLE_OAUTH=true -ENABLE_ADMIN_INTERFACE=true -ENABLE_MONITORING=false -ENABLE_METRICS=false -DEBUG_MODE=true -LOG_LEVEL=debug -LOG_FORMAT=json -CACHE_TTL=1800 -REQUEST_TIMEOUT=60000 -MAX_CONNECTIONS=150 -ENABLE_CORS=true -CORS_ORIGIN=https://staging.rxminder.com -FRONTEND_REPLICAS=2 -DATABASE_REPLICAS=1 -FRONTEND_MEMORY_REQUEST=256Mi -FRONTEND_CPU_REQUEST=100m -FRONTEND_MEMORY_LIMIT=512Mi -FRONTEND_CPU_LIMIT=500m -DATABASE_MEMORY_REQUEST=512Mi -DATABASE_CPU_REQUEST=200m -DATABASE_MEMORY_LIMIT=1Gi -DATABASE_CPU_LIMIT=1000m diff --git a/k8s-kustomize/overlays/staging/kustomization.yaml b/k8s-kustomize/overlays/staging/kustomization.yaml deleted file mode 100644 index fe1efea..0000000 --- a/k8s-kustomize/overlays/staging/kustomization.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: rxminder-staging - -# Reference the base configuration -resources: - - ../../base - - namespace.yaml - -# Override namespace for staging -namespace: rxminder-staging - -# Staging-specific labels -labels: - - pairs: - environment: staging - tier: staging - -# Staging image tags and configurations -images: - - name: frontend-image - newName: gitea-http.taildb3494.ts.net/will/meds - newTag: latest - - name: couchdb-image - newName: couchdb - newTag: 3.3.2 - -# Staging replicas -replicas: - - name: rxminder-frontend - count: 2 - - name: rxminder-couchdb - count: 1 - -# Environment-specific patches -patches: - # Resource limits - - target: - kind: Deployment - name: rxminder-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - - - target: - kind: StatefulSet - name: rxminder-couchdb - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "512Mi" - cpu: "200m" - limits: - memory: "1Gi" - cpu: "1000m" - -# ConfigMap generation -configMapGenerator: - - name: rxminder-config - envs: - - config.env - behavior: create diff --git a/k8s/README.md b/k8s/README.md deleted file mode 100644 index 41c8e39..0000000 --- a/k8s/README.md +++ /dev/null @@ -1,185 +0,0 @@ -# Kubernetes Manifests for RxMinder - -This directory contains Kubernetes manifests and templates for deploying RxMinder on a Kubernetes cluster. - -## 🎯 Template-Based Deployment (Recommended) - -RxMinder uses **template files** with environment variable substitution for secure, user-friendly deployment. - -### **Template Files** - -- `couchdb-secret.yaml.template` - Database credentials (uses `stringData` - no base64 encoding needed!) -- `ingress.yaml.template` - Ingress configuration with customizable hostname -- `configmap.yaml.template` - Application configuration -- `frontend-deployment.yaml.template` - Frontend deployment - -### **Static Files** - -- `couchdb-statefulset.yaml` - StatefulSet for CouchDB database -- `couchdb-service.yaml` - Service to expose CouchDB -- `couchdb-pvc.yaml` - PersistentVolumeClaim for CouchDB storage -- `db-seed-job.yaml` - Job to seed initial database data -- `frontend-service.yaml` - Service to expose frontend -- `hpa.yaml` - Horizontal Pod Autoscaler -- `network-policy.yaml` - Network security policies - -## πŸš€ Deployment Instructions - -### **Option 1: Template-Based Deployment (Recommended)** - -```bash -# 1. Copy and configure environment -cp .env.example .env - -# 2. Edit .env with your settings -nano .env -# Set: APP_NAME, COUCHDB_PASSWORD, INGRESS_HOST, etc. - -# 3. Deploy with templates -./scripts/k8s-deploy-template.sh deploy - -# 4. Check status -./scripts/k8s-deploy-template.sh status - -# 5. Cleanup (if needed) -./scripts/k8s-deploy-template.sh delete -``` - -**Benefits of template approach:** - -- βœ… No manual base64 encoding required -- βœ… Secure credential management via `.env` -- βœ… Automatic dependency ordering -- βœ… Built-in validation and status checking -- βœ… Easy customization of app name and configuration - -### **Option 2: Manual Deployment** - -For advanced users who want manual control: - -```bash -# Manual template processing (requires envsubst) -envsubst < couchdb-secret.yaml.template > /tmp/couchdb-secret.yaml -envsubst < ingress.yaml.template > /tmp/ingress.yaml - -# Apply resources in order -kubectl apply -f /tmp/couchdb-secret.yaml -kubectl apply -f couchdb-pvc.yaml -kubectl apply -f couchdb-service.yaml -kubectl apply -f couchdb-statefulset.yaml -kubectl apply -f configmap.yaml.template -kubectl apply -f frontend-deployment.yaml.template -kubectl apply -f frontend-service.yaml -kubectl apply -f /tmp/ingress.yaml -kubectl apply -f network-policy.yaml -kubectl apply -f hpa.yaml -kubectl apply -f db-seed-job.yaml -``` - -### **Environment Configuration** - -Create `.env` with these required variables: - -```bash -# Application Configuration -APP_NAME=rxminder # Customize your app name -INGRESS_HOST=rxminder.yourdomain.com # Your external hostname - -# Docker Image Configuration -DOCKER_IMAGE=myregistry.com/rxminder:v1.0.0 # Your container image - -# Database Credentials -COUCHDB_USER=admin -COUCHDB_PASSWORD=super-secure-password-123 - -# Storage Configuration -STORAGE_CLASS=longhorn # Your cluster's storage class -STORAGE_SIZE=20Gi # Database storage allocation - -# Optional: Advanced Configuration -VITE_COUCHDB_URL=http://localhost:5984 -APP_BASE_URL=https://rxminder.yourdomain.com -``` - -### **Docker Image Options** - -Configure the container image based on your registry: - -| Registry Type | Example Image | Use Case | -| ----------------------------- | -------------------------------------------------------------- | ------------------ | -| **Docker Hub** | `rxminder/rxminder:v1.0.0` | Public releases | -| **GitHub Container Registry** | `ghcr.io/username/rxminder:latest` | GitHub integration | -| **AWS ECR** | `123456789012.dkr.ecr.us-west-2.amazonaws.com/rxminder:v1.0.0` | AWS deployments | -| **Google GCR** | `gcr.io/project-id/rxminder:stable` | Google Cloud | -| **Private Registry** | `registry.company.com/rxminder:production` | Enterprise | -| **Local Registry** | `localhost:5000/rxminder:dev` | Development | - -### **Storage Class Options** - -Choose the appropriate storage class for your environment: - -| Platform | Recommended Storage Class | Notes | -| --------------------------- | ------------------------- | -------------------------------- | -| **Raspberry Pi + Longhorn** | `longhorn` | Distributed storage across nodes | -| **k3s** | `local-path` | Single-node local storage | -| **AWS EKS** | `gp3` or `gp2` | General Purpose SSD | -| **Google GKE** | `pd-ssd` | SSD Persistent Disk | -| **Azure AKS** | `managed-premium` | Premium SSD | - -**Check available storage classes:** - -```bash -kubectl get storageclass -``` - -```bash -# Kubernetes Ingress Configuration -INGRESS_HOST=app.meds.192.168.1.100.nip.io # Your cluster IP - -# For production with custom domain -INGRESS_HOST=meds.yourdomain.com -``` - -## Credentials - -The CouchDB credentials are stored in a Kubernetes secret. **IMPORTANT**: Update the credentials in `couchdb-secret.yaml` with your own secure values before deploying to production. - -## Architecture - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Frontend Pod β”‚ -β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚ -β”‚ β”‚ React Application β”‚β”‚ -β”‚ β”‚ β€’ Authentication Service β”‚β”‚ ← Embedded in frontend -β”‚ β”‚ β€’ UI Components β”‚β”‚ -β”‚ β”‚ β€’ Medication Management β”‚β”‚ -β”‚ β”‚ β€’ Email Integration β”‚β”‚ -β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - ↓ HTTP API -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ CouchDB StatefulSet β”‚ -β”‚ β€’ User Data & Authentication β”‚ -β”‚ β€’ Medication Records β”‚ -β”‚ β€’ Persistent Storage β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` - -**Key Features:** - -- **Monolithic Frontend**: Single container with all functionality -- **Database**: CouchDB running as a StatefulSet with persistent storage -- **Storage**: Longhorn for persistent volume management -- **Networking**: Services configured for proper communication between components - -## Raspberry Pi Compatibility - -All manifests use multi-architecture images and are optimized for ARM architecture commonly used in Raspberry Pi clusters. - -## Important Notes - -- The PVC uses Longhorn storage class for persistent storage -- CouchDB runs as a StatefulSet for stable network identifiers -- Frontend is exposed via LoadBalancer service -- CouchDB is exposed via ClusterIP service (internal access only) diff --git a/k8s/configmap.yaml b/k8s/configmap.yaml deleted file mode 100644 index c273dd4..0000000 --- a/k8s/configmap.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: ${APP_NAME:-rxminder}-config - labels: - app: ${APP_NAME:-rxminder} -data: - NODE_ENV: 'production' - REACT_APP_API_URL: 'http://couchdb-service:5984' - LOG_LEVEL: 'info' diff --git a/k8s/configmap.yaml.template b/k8s/configmap.yaml.template deleted file mode 100644 index ccef00a..0000000 --- a/k8s/configmap.yaml.template +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: ${APP_NAME}-config - labels: - app: ${APP_NAME} -data: - NODE_ENV: "production" - REACT_APP_API_URL: "http://${APP_NAME}-couchdb-service:5984" - LOG_LEVEL: "info" diff --git a/k8s/couchdb-pvc.yaml.template b/k8s/couchdb-pvc.yaml.template deleted file mode 100644 index 6df65ac..0000000 --- a/k8s/couchdb-pvc.yaml.template +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ${APP_NAME}-couchdb-pvc - labels: - app: ${APP_NAME} - component: database -spec: - accessModes: - - ReadWriteOnce - storageClassName: ${STORAGE_CLASS} - resources: - requests: - storage: ${STORAGE_SIZE} diff --git a/k8s/couchdb-secret.yaml b/k8s/couchdb-secret.yaml deleted file mode 100644 index 8f9437a..0000000 --- a/k8s/couchdb-secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: couchdb-secret - labels: - app: ${APP_NAME:-rxminder} - component: database -type: Opaque -stringData: - # These values will be automatically base64 encoded by Kubernetes - # Update these in your .env file before deployment - username: ${COUCHDB_USER:-admin} - password: ${COUCHDB_PASSWORD:-change-this-secure-password} diff --git a/k8s/couchdb-secret.yaml.template b/k8s/couchdb-secret.yaml.template deleted file mode 100644 index 6c20a9f..0000000 --- a/k8s/couchdb-secret.yaml.template +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: couchdb-secret - labels: - app: ${APP_NAME} - component: database -type: Opaque -stringData: - # These values will be automatically base64 encoded by Kubernetes - # Update these in your .env file before deployment - username: ${COUCHDB_USER} - password: ${COUCHDB_PASSWORD} diff --git a/k8s/couchdb-service.yaml b/k8s/couchdb-service.yaml deleted file mode 100644 index acd9e03..0000000 --- a/k8s/couchdb-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ${APP_NAME}-couchdb-service - labels: - app: ${APP_NAME} - component: database -spec: - selector: - app: ${APP_NAME} - component: database - ports: - - name: couchdb - port: 5984 - targetPort: 5984 - protocol: TCP - type: ClusterIP diff --git a/k8s/couchdb-service.yaml.template b/k8s/couchdb-service.yaml.template deleted file mode 100644 index acd9e03..0000000 --- a/k8s/couchdb-service.yaml.template +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ${APP_NAME}-couchdb-service - labels: - app: ${APP_NAME} - component: database -spec: - selector: - app: ${APP_NAME} - component: database - ports: - - name: couchdb - port: 5984 - targetPort: 5984 - protocol: TCP - type: ClusterIP diff --git a/k8s/couchdb-statefulset.yaml b/k8s/couchdb-statefulset.yaml deleted file mode 100644 index ce06627..0000000 --- a/k8s/couchdb-statefulset.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: ${APP_NAME}-couchdb - labels: - app: ${APP_NAME} - component: database -spec: - serviceName: ${APP_NAME}-couchdb-service - replicas: 1 - selector: - matchLabels: - app: ${APP_NAME} - component: database - template: - metadata: - labels: - app: ${APP_NAME} - component: database - spec: - containers: - - name: couchdb - image: couchdb:3.3.2 - ports: - - containerPort: 5984 - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - resources: - requests: - memory: '64Mi' - cpu: '30m' - limits: - memory: '128Mi' - cpu: '60m' - volumeMounts: - - name: couchdb-data - mountPath: /opt/couchdb/data - livenessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 60 - periodSeconds: 30 - readinessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 10 - periodSeconds: 5 - volumeClaimTemplates: - - metadata: - name: couchdb-data - labels: - app: ${APP_NAME} - component: database - spec: - accessModes: ['ReadWriteOnce'] - storageClassName: ${STORAGE_CLASS} - resources: - requests: - storage: ${STORAGE_SIZE} diff --git a/k8s/couchdb-statefulset.yaml.template b/k8s/couchdb-statefulset.yaml.template deleted file mode 100644 index c9cd296..0000000 --- a/k8s/couchdb-statefulset.yaml.template +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: ${APP_NAME}-couchdb - labels: - app: ${APP_NAME} - component: database -spec: - serviceName: ${APP_NAME}-couchdb-service - replicas: 1 - selector: - matchLabels: - app: ${APP_NAME} - component: database - template: - metadata: - labels: - app: ${APP_NAME} - component: database - spec: - containers: - - name: couchdb - image: couchdb:3.3.2 - ports: - - containerPort: 5984 - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - resources: - requests: - memory: "64Mi" - cpu: "30m" - limits: - memory: "128Mi" - cpu: "60m" - volumeMounts: - - name: couchdb-data - mountPath: /opt/couchdb/data - livenessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 60 - periodSeconds: 30 - readinessProbe: - httpGet: - path: /_up - port: 5984 - initialDelaySeconds: 10 - periodSeconds: 5 - volumeClaimTemplates: - - metadata: - name: couchdb-data - labels: - app: ${APP_NAME} - component: database - spec: - accessModes: ["ReadWriteOnce"] - storageClassName: ${STORAGE_CLASS} - resources: - requests: - storage: ${STORAGE_SIZE} diff --git a/k8s/db-seed-job.yaml b/k8s/db-seed-job.yaml deleted file mode 100644 index 8442c47..0000000 --- a/k8s/db-seed-job.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: db-seed - labels: - app: rxminder - component: database -spec: - template: - metadata: - labels: - app: rxminder - component: database - spec: - containers: - - name: db-seeder - image: couchdb:3.3.2 - env: - - name: COUCHDB_USER - valueFrom: - secretKeyRef: - name: couchdb-secret - key: username - - name: COUCHDB_PASSWORD - valueFrom: - secretKeyRef: - name: couchdb-secret - key: password - command: ['/bin/sh', '-c'] - 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" - restartPolicy: Never - backoffLimit: 4 diff --git a/k8s/frontend-deployment.yaml b/k8s/frontend-deployment.yaml deleted file mode 100644 index fef604d..0000000 --- a/k8s/frontend-deployment.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ${APP_NAME}-frontend - labels: - app: ${APP_NAME} - component: frontend -spec: - replicas: 1 - selector: - matchLabels: - app: ${APP_NAME} - component: frontend - template: - metadata: - labels: - app: ${APP_NAME} - component: frontend - spec: - containers: - - name: frontend - image: ${DOCKER_IMAGE} - ports: - - containerPort: 80 - envFrom: - - configMapRef: - name: ${APP_NAME}-config - resources: - requests: - memory: '32Mi' - cpu: '20m' - limits: - memory: '64Mi' - cpu: '40m' - livenessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 30 - periodSeconds: 30 - readinessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 5 - periodSeconds: 5 diff --git a/k8s/frontend-deployment.yaml.template b/k8s/frontend-deployment.yaml.template deleted file mode 100644 index a6635c3..0000000 --- a/k8s/frontend-deployment.yaml.template +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: ${APP_NAME}-frontend - labels: - app: ${APP_NAME} - component: frontend -spec: - replicas: 1 - selector: - matchLabels: - app: ${APP_NAME} - component: frontend - template: - metadata: - labels: - app: ${APP_NAME} - component: frontend - spec: - imagePullSecrets: - - name: ${APP_NAME}-registry-secret - containers: - - name: frontend - image: ${DOCKER_IMAGE} - ports: - - containerPort: 80 - envFrom: - - configMapRef: - name: ${APP_NAME}-config - resources: - requests: - memory: "32Mi" - cpu: "20m" - limits: - memory: "64Mi" - cpu: "40m" - livenessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 30 - periodSeconds: 30 - readinessProbe: - httpGet: - path: / - port: 80 - initialDelaySeconds: 5 - periodSeconds: 5 diff --git a/k8s/frontend-service.yaml b/k8s/frontend-service.yaml deleted file mode 100644 index 9eeb3e1..0000000 --- a/k8s/frontend-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ${APP_NAME}-frontend-service - labels: - app: ${APP_NAME} - component: frontend -spec: - selector: - app: ${APP_NAME} - component: frontend - ports: - - name: http - port: 80 - targetPort: 80 - protocol: TCP - type: ClusterIP diff --git a/k8s/frontend-service.yaml.template b/k8s/frontend-service.yaml.template deleted file mode 100644 index 9eeb3e1..0000000 --- a/k8s/frontend-service.yaml.template +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ${APP_NAME}-frontend-service - labels: - app: ${APP_NAME} - component: frontend -spec: - selector: - app: ${APP_NAME} - component: frontend - ports: - - name: http - port: 80 - targetPort: 80 - protocol: TCP - type: ClusterIP diff --git a/k8s/hpa.yaml b/k8s/hpa.yaml deleted file mode 100644 index afd4aa1..0000000 --- a/k8s/hpa.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: frontend-hpa - labels: - app: rxminder - component: frontend -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: frontend - minReplicas: 1 - maxReplicas: 3 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 50 diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml deleted file mode 100644 index 074f101..0000000 --- a/k8s/ingress.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: frontend-ingress - labels: - app: rxminder - component: frontend - annotations: {} - # Add SSL redirect if using HTTPS - # nginx.ingress.kubernetes.io/ssl-redirect: "true" - # cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - ingressClassName: nginx - # Uncomment for HTTPS with cert-manager - # tls: - # - hosts: - # - ${INGRESS_HOST} - # secretName: frontend-tls - rules: - - host: app.meds.192.168.153.243.nip.io # TODO: Make configurable via deployment script - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: frontend-service - port: - number: 80 diff --git a/k8s/ingress.yaml.template b/k8s/ingress.yaml.template deleted file mode 100644 index 9f024b4..0000000 --- a/k8s/ingress.yaml.template +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ${APP_NAME}-ingress - labels: - app: ${APP_NAME} - component: frontend - annotations: - # Add SSL redirect if using HTTPS - # nginx.ingress.kubernetes.io/ssl-redirect: "true" - # cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - ingressClassName: nginx - # Uncomment for HTTPS with cert-manager - # tls: - # - hosts: - # - ${INGRESS_HOST} - # secretName: frontend-tls - rules: - - host: ${INGRESS_HOST} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: ${APP_NAME}-frontend-service - port: - number: 80 diff --git a/k8s/network-policy.yaml b/k8s/network-policy.yaml deleted file mode 100644 index 457001b..0000000 --- a/k8s/network-policy.yaml +++ /dev/null @@ -1,68 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: frontend-policy - labels: - app: rxminder - component: frontend -spec: - podSelector: - matchLabels: - component: frontend - policyTypes: - - Ingress - - Egress - ingress: - - from: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 80 - egress: - - to: - - podSelector: - matchLabels: - component: database - ports: - - protocol: TCP - port: 5984 - - to: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 80 ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: database-policy - labels: - app: rxminder - component: database -spec: - podSelector: - matchLabels: - component: database - policyTypes: - - Ingress - - Egress - ingress: - - from: - - podSelector: - matchLabels: - component: frontend - ports: - - protocol: TCP - port: 5984 - egress: - - to: - - podSelector: - matchLabels: - component: database - ports: - - protocol: TCP - port: 5984 diff --git a/k8s/registry-secret.yaml.template b/k8s/registry-secret.yaml.template deleted file mode 100644 index c5aed16..0000000 --- a/k8s/registry-secret.yaml.template +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: ${APP_NAME}-registry-secret - labels: - app: ${APP_NAME} - component: registry -type: kubernetes.io/dockerconfigjson -data: - .dockerconfigjson: ${REGISTRY_AUTH_BASE64} diff --git a/scripts/buildx-helper.sh b/scripts/buildx-helper.sh deleted file mode 100755 index c661a2e..0000000 --- a/scripts/buildx-helper.sh +++ /dev/null @@ -1,453 +0,0 @@ -#!/bin/bash - -# Docker Buildx Helper Script -# Provides multi-platform Docker image building and pushing capabilities - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Configuration -BUILDER_NAME="rxminder-builder" -PLATFORMS="linux/amd64,linux/arm64" -DOCKERFILE_PATH="docker/Dockerfile" -DOCKER_CONTEXT="." -IMAGE_NAME="${DOCKER_IMAGE_NAME:-rxminder}" -REGISTRY="${DOCKER_REGISTRY:-}" -TAG="${DOCKER_TAG:-latest}" - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Function to show usage -show_usage() { - echo "Docker Buildx Helper Script" - echo "" - echo "Usage: $0 [options]" - echo "" - echo "Commands:" - echo " setup Setup buildx builder instance" - echo " build-local Build image for local platform only" - echo " build-multi Build multi-platform images (production)" - echo " build-multi-dev Build multi-platform images (development)" - echo " build-push Build and push multi-platform images" - echo " push Push existing images to registry" - echo " inspect Inspect builder instance" - echo " bake Build using docker-bake.hcl" - echo " cleanup Remove builder instance and cleanup" - echo " list List available builders" - echo "" - echo "Environment Variables:" - echo " DOCKER_IMAGE_NAME Image name (default: rxminder)" - echo " DOCKER_REGISTRY Registry URL (e.g., ghcr.io/username)" - echo " DOCKER_TAG Image tag (default: latest)" - echo " APP_NAME Application name for build args" - echo " NODE_ENV Build environment (development/production)" - echo "" - echo "Examples:" - echo " $0 setup" - echo " $0 build-local" - echo " DOCKER_REGISTRY=ghcr.io/myuser $0 build-push" - echo " DOCKER_TAG=v1.0.0 $0 build-multi" -} - -# Function to setup buildx builder -setup_builder() { - print_status "Setting up Docker Buildx builder..." - - # Check if buildx is available - if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available. Please update Docker." - exit 1 - fi - - # Remove existing builder if it exists - if docker buildx ls | grep -q "$BUILDER_NAME"; then - print_status "Removing existing builder instance..." - docker buildx rm "$BUILDER_NAME" 2>/dev/null || true - fi - - # Create new builder instance - print_status "Creating new buildx builder instance: $BUILDER_NAME" - docker buildx create \ - --name "$BUILDER_NAME" \ - --driver docker-container \ - --platform "$PLATFORMS" \ - --bootstrap - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Inspect the builder - docker buildx inspect --bootstrap - - print_success "Buildx builder setup completed!" -} - -# Function to get build arguments -get_build_args() { - local build_env="${1:-${NODE_ENV:-production}}" - echo "--build-arg APP_NAME=${APP_NAME:-RxMinder}" - echo "--build-arg NODE_ENV=${build_env}" - echo "--build-arg JWT_SECRET=${JWT_SECRET:-demo_jwt_secret_for_frontend_only}" - echo "--build-arg SESSION_SECRET=${SESSION_SECRET:-demo_session_secret_for_frontend_only}" - echo "--build-arg VITE_COUCHDB_URL=${VITE_COUCHDB_URL:-http://couchdb:5984}" - echo "--build-arg VITE_COUCHDB_USER=${VITE_COUCHDB_USER:-admin}" - echo "--build-arg VITE_COUCHDB_PASSWORD=${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" - echo "--build-arg APP_BASE_URL=${APP_BASE_URL:-http://localhost:8080}" - echo "--build-arg VITE_GOOGLE_CLIENT_ID=${VITE_GOOGLE_CLIENT_ID:-}" - echo "--build-arg VITE_GITHUB_CLIENT_ID=${VITE_GITHUB_CLIENT_ID:-}" - echo "--build-arg MAILGUN_API_KEY=${MAILGUN_API_KEY:-}" - echo "--build-arg MAILGUN_DOMAIN=${MAILGUN_DOMAIN:-}" - echo "--build-arg MAILGUN_FROM_EMAIL=${MAILGUN_FROM_EMAIL:-}" -} - -# Function to get image tags -get_image_tags() { - local base_name="$1" - local tags="" - - # Always include the specified tag - tags="$tags -t $base_name:$TAG" - - # Add latest tag if not already latest - if [ "$TAG" != "latest" ]; then - tags="$tags -t $base_name:latest" - fi - - # Add git-based tags if in git repo - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - local git_hash=$(git rev-parse --short HEAD) - local git_branch=$(git rev-parse --abbrev-ref HEAD | sed 's/[^a-zA-Z0-9.-]/-/g') - - tags="$tags -t $base_name:$git_hash" - - if [ "$git_branch" != "HEAD" ] && [ "$git_branch" != "main" ] && [ "$git_branch" != "master" ]; then - tags="$tags -t $base_name:$git_branch" - fi - fi - - echo "$tags" -} - -# Function to build for local platform only -build_local() { - print_status "Building Docker image for local platform..." - - # Ensure builder is available - if ! docker buildx ls | grep -q "$BUILDER_NAME"; then - print_warning "Builder not found, setting up..." - setup_builder - fi - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Get the current platform - local platform=$(docker version --format '{{.Server.Os}}/{{.Server.Arch}}') - print_status "Building for platform: $platform" - - # Determine image name - local image_name="$IMAGE_NAME" - if [ -n "$REGISTRY" ]; then - image_name="$REGISTRY/$IMAGE_NAME" - fi - - # Get build arguments and tags (use development for local builds) - local build_args=$(get_build_args "development") - local tags=$(get_image_tags "$image_name") - - print_status "Building image: $image_name:$TAG (development mode)" - - # Build the image - docker buildx build \ - --platform "$platform" \ - $build_args \ - $tags \ - --load \ - -f "$DOCKERFILE_PATH" \ - "$DOCKER_CONTEXT" - - print_success "Local build completed successfully!" - - # Show image info - docker images "$image_name" | head -2 -} - -# Function to build multi-platform images -build_multi() { - print_status "Building multi-platform Docker images..." - - # Ensure builder is available - if ! docker buildx ls | grep -q "$BUILDER_NAME"; then - print_warning "Builder not found, setting up..." - setup_builder - fi - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Determine image name - local image_name="$IMAGE_NAME" - if [ -n "$REGISTRY" ]; then - image_name="$REGISTRY/$IMAGE_NAME" - fi - - # Get build arguments and tags (use production for multi-platform) - local build_args=$(get_build_args "production") - local tags=$(get_image_tags "$image_name") - - print_status "Building for platforms: $PLATFORMS" - print_status "Image: $image_name:$TAG (production mode)" - - # Build the images - docker buildx build \ - --platform "$PLATFORMS" \ - $build_args \ - $tags \ - -f "$DOCKERFILE_PATH" \ - "$DOCKER_CONTEXT" - - print_success "Multi-platform build completed successfully!" -} - -# Function to build multi-platform images for development -build_multi_dev() { - print_status "Building multi-platform Docker images for development..." - - # Ensure builder is available - if ! docker buildx ls | grep -q "$BUILDER_NAME"; then - print_warning "Builder not found, setting up..." - setup_builder - fi - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Determine image name - local image_name="$IMAGE_NAME" - if [ -n "$REGISTRY" ]; then - image_name="$REGISTRY/$IMAGE_NAME" - fi - - # Get build arguments and tags (use development for multi-platform dev) - local build_args=$(get_build_args "development") - local tags=$(get_image_tags "$image_name") - - print_status "Building for platforms: $PLATFORMS" - print_status "Image: $image_name:$TAG (development mode)" - - # Build the images - docker buildx build \ - --platform "$PLATFORMS" \ - $build_args \ - $tags \ - -f "$DOCKERFILE_PATH" \ - "$DOCKER_CONTEXT" - - print_success "Multi-platform development build completed successfully!" -} - -# Function to build and push multi-platform images -build_push() { - print_status "Building and pushing multi-platform Docker images..." - - if [ -z "$REGISTRY" ]; then - print_error "DOCKER_REGISTRY environment variable must be set for pushing" - print_status "Example: DOCKER_REGISTRY=ghcr.io/username $0 build-push" - exit 1 - fi - - # Ensure builder is available - if ! docker buildx ls | grep -q "$BUILDER_NAME"; then - print_warning "Builder not found, setting up..." - setup_builder - fi - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Determine image name - local image_name="$REGISTRY/$IMAGE_NAME" - - # Get build arguments and tags (use production for build-push) - local build_args=$(get_build_args "production") - local tags=$(get_image_tags "$image_name") - - print_status "Building and pushing for platforms: $PLATFORMS" - print_status "Registry: $REGISTRY" - print_status "Image: $image_name:$TAG (production mode)" - - # Build and push the images - docker buildx build \ - --platform "$PLATFORMS" \ - $build_args \ - $tags \ - --push \ - -f "$DOCKERFILE_PATH" \ - "$DOCKER_CONTEXT" - - print_success "Multi-platform build and push completed successfully!" - - # Show pushed images - echo "" - print_status "Pushed images:" - echo "$image_name:$TAG" - if [ "$TAG" != "latest" ]; then - echo "$image_name:latest" - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - local git_hash=$(git rev-parse --short HEAD) - echo "$image_name:$git_hash" - fi -} - -# Function to push existing images -push_images() { - print_status "Pushing existing images to registry..." - - if [ -z "$REGISTRY" ]; then - print_error "DOCKER_REGISTRY environment variable must be set for pushing" - exit 1 - fi - - local image_name="$REGISTRY/$IMAGE_NAME" - - print_status "Pushing $image_name:$TAG" - docker push "$image_name:$TAG" - - if [ "$TAG" != "latest" ]; then - print_status "Pushing $image_name:latest" - docker push "$image_name:latest" - fi - - print_success "Push completed successfully!" -} - -# Function to inspect builder -inspect_builder() { - print_status "Inspecting buildx builder..." - - if docker buildx ls | grep -q "$BUILDER_NAME"; then - docker buildx inspect "$BUILDER_NAME" - else - print_warning "Builder '$BUILDER_NAME' not found" - print_status "Available builders:" - docker buildx ls - fi -} - -# Function to build using docker-bake.hcl -build_bake() { - print_status "Building using docker-bake.hcl..." - - if [ ! -f "docker-bake.hcl" ]; then - print_error "docker-bake.hcl not found" - exit 1 - fi - - # Ensure builder is available - if ! docker buildx ls | grep -q "$BUILDER_NAME"; then - print_warning "Builder not found, setting up..." - setup_builder - fi - - # Use the builder - docker buildx use "$BUILDER_NAME" - - # Build using bake - docker buildx bake -f docker-bake.hcl - - print_success "Bake build completed successfully!" -} - -# Function to cleanup builder -cleanup_builder() { - print_status "Cleaning up buildx builder..." - - if docker buildx ls | grep -q "$BUILDER_NAME"; then - docker buildx rm "$BUILDER_NAME" - print_success "Builder '$BUILDER_NAME' removed" - else - print_warning "Builder '$BUILDER_NAME' not found" - fi - - # Cleanup unused build cache - print_status "Cleaning up build cache..." - docker buildx prune -f - - print_success "Cleanup completed!" -} - -# Function to list builders -list_builders() { - print_status "Available buildx builders:" - docker buildx ls -} - -# Main script logic -case "${1:-}" in - "setup") - setup_builder - ;; - "build-local") - build_local - ;; - "build-multi") - build_multi - ;; - "build-multi-dev") - build_multi_dev - ;; - "build-push") - build_push - ;; - "push") - push_images - ;; - "inspect") - inspect_builder - ;; - "bake") - build_bake - ;; - "cleanup") - cleanup_builder - ;; - "list") - list_builders - ;; - "help"|"--help"|"-h") - show_usage - ;; - "") - print_error "No command specified" - show_usage - exit 1 - ;; - *) - print_error "Unknown command: $1" - show_usage - exit 1 - ;; -esac diff --git a/scripts/cleanup-tests.sh b/scripts/cleanup-tests.sh deleted file mode 100755 index 881ced8..0000000 --- a/scripts/cleanup-tests.sh +++ /dev/null @@ -1,402 +0,0 @@ -#!/bin/bash - -# Test Cleanup Script -# Removes redundant test files and reorganizes test structure - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -TESTS_DIR="$PROJECT_ROOT/tests" - -# Colors for output -BLUE='\033[34m' -GREEN='\033[32m' -YELLOW='\033[33m' -RED='\033[31m' -RESET='\033[0m' - -echo -e "${BLUE}πŸ§ͺ Cleaning up test files and structure...${RESET}" - -# Function to backup files before deletion -backup_file() { - local file="$1" - local backup_dir="$TESTS_DIR/.backup" - - if [ -f "$file" ]; then - mkdir -p "$backup_dir" - cp "$file" "$backup_dir/$(basename "$file").$(date +%Y%m%d-%H%M%S)" - echo -e "${YELLOW} Backed up: $(basename "$file")${RESET}" - fi -} - -# Function to remove redundant files -remove_redundant_files() { - echo -e "${BLUE}πŸ—‘οΈ Removing redundant manual test files...${RESET}" - - # Manual test files that can be replaced by E2E tests - local manual_files=( - "$TESTS_DIR/manual/admin-login-debug.js" - "$TESTS_DIR/manual/auth-db-debug.js" - "$TESTS_DIR/manual/debug-email-validation.js" - ) - - for file in "${manual_files[@]}"; do - if [ -f "$file" ]; then - backup_file "$file" - rm "$file" - echo -e "${RED} Removed: $(basename "$file") (replaced by E2E tests)${RESET}" - fi - done - - # Remove manual directory if empty - if [ -d "$TESTS_DIR/manual" ] && [ -z "$(ls -A "$TESTS_DIR/manual")" ]; then - rmdir "$TESTS_DIR/manual" - echo -e "${RED} Removed empty manual directory${RESET}" - fi -} - -# Function to consolidate mock files -consolidate_mocks() { - echo -e "${BLUE}πŸ”§ Consolidating mock files...${RESET}" - - # Check if mocks can be simplified - local mock_files=( - "$TESTS_DIR/__mocks__/node-fetch.js" - "$TESTS_DIR/__mocks__/uuid.js" - ) - - # Create a consolidated mock index - cat > "$TESTS_DIR/__mocks__/index.js" << 'EOF' -// Consolidated mock exports -module.exports = { - fetch: require('./node-fetch'), - uuid: require('./uuid'), -}; -EOF - - echo -e "${GREEN} Created consolidated mock index${RESET}" -} - -# Function to clean up redundant E2E test patterns -clean_e2e_tests() { - echo -e "${BLUE}🎭 Optimizing E2E test structure...${RESET}" - - # Check for duplicate test patterns in E2E files - local e2e_dir="$TESTS_DIR/e2e" - - if [ -d "$e2e_dir" ]; then - # Remove redundant beforeEach blocks that are identical - echo -e "${YELLOW} Analyzing E2E test patterns...${RESET}" - - # Create optimized test utilities - cat > "$e2e_dir/test-utils.ts" << 'EOF' -// Optimized test utilities to reduce duplication -import { Page } from '@playwright/test'; - -export class TestUtils { - static async loginAsAdmin(page: Page): Promise { - await page.goto('/'); - await page.fill('input[type="email"]', 'admin@localhost'); - await page.fill('input[type="password"]', 'admin123!'); - await page.click('button[type="submit"]'); - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async loginAsUser(page: Page, email: string = 'testuser@example.com', password: string = 'TestPassword123!'): Promise { - await page.goto('/'); - await page.fill('input[type="email"]', email); - await page.fill('input[type="password"]', password); - await page.click('button[type="submit"]'); - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async waitForApp(page: Page): Promise { - await page.waitForSelector('h1:has-text("Medication Reminder")'); - } - - static async openModal(page: Page, buttonText: string): Promise { - await page.click(`button:has-text("${buttonText}")`); - } - - static async closeModal(page: Page): Promise { - await page.click('button:has-text("Close")'); - } -} -EOF - - echo -e "${GREEN} Created optimized test utilities${RESET}" - fi -} - -# Function to clean up integration tests -clean_integration_tests() { - echo -e "${BLUE}πŸ”— Reviewing integration tests...${RESET}" - - local integration_dir="$TESTS_DIR/integration" - - if [ -d "$integration_dir" ]; then - # Keep production.test.js as it's useful for deployment validation - echo -e "${GREEN} Integration tests are properly structured${RESET}" - - # Add a test runner script for integration tests - cat > "$integration_dir/run-integration.sh" << 'EOF' -#!/bin/bash - -# Integration Test Runner -# Ensures services are running before running integration tests - -echo "πŸ” Checking service availability..." - -# Check CouchDB -if ! curl -s http://localhost:5984/ > /dev/null 2>&1; then - echo "❌ CouchDB not available at localhost:5984" - exit 1 -fi - -# Check Frontend -if ! curl -s http://localhost:8080/ > /dev/null 2>&1; then - echo "⚠️ Frontend not available at localhost:8080" - echo " Starting services..." - # Could add auto-start logic here -fi - -echo "βœ… Services are available" -echo "πŸ§ͺ Running integration tests..." - -bun run test:integration -EOF - - chmod +x "$integration_dir/run-integration.sh" - echo -e "${GREEN} Created integration test runner${RESET}" - fi -} - -# Function to update test documentation -update_test_docs() { - echo -e "${BLUE}πŸ“š Updating test documentation...${RESET}" - - # Create an updated README with cleanup information - cat > "$TESTS_DIR/README-CLEANUP.md" << 'EOF' -# 🧹 Test Structure Cleanup - -## Changes Made - -### ❌ Removed Files -- `manual/admin-login-debug.js` β†’ Replaced by `e2e/auth-debug.spec.ts` -- `manual/auth-db-debug.js` β†’ Replaced by automated E2E tests -- `manual/debug-email-validation.js` β†’ Integrated into auth E2E tests - -### βœ… Optimizations -- Consolidated mock files with index.js -- Created shared test utilities in `e2e/test-utils.ts` -- Added integration test runner script -- Removed duplicate test patterns - -### πŸ“ Current Structure -``` -tests/ -β”œβ”€β”€ __mocks__/ # Consolidated mocks -β”‚ β”œβ”€β”€ index.js # ✨ New: Mock aggregator -β”‚ β”œβ”€β”€ node-fetch.js # HTTP mocking -β”‚ └── uuid.js # UUID mocking -β”œβ”€β”€ integration/ # Service integration tests -β”‚ β”œβ”€β”€ production.test.js # Production readiness -β”‚ └── run-integration.sh # ✨ New: Test runner -β”œβ”€β”€ e2e/ # End-to-end tests -β”‚ β”œβ”€β”€ auth-debug.spec.ts # ✨ New: Replaces manual auth tests -β”‚ β”œβ”€β”€ test-utils.ts # ✨ New: Shared utilities -β”‚ β”œβ”€β”€ auth.spec.ts # Authentication flows -β”‚ β”œβ”€β”€ medication.spec.ts # Medication management -β”‚ β”œβ”€β”€ admin.spec.ts # Admin interface -β”‚ β”œβ”€β”€ ui-navigation.spec.ts # UI and navigation -β”‚ β”œβ”€β”€ reminders.spec.ts # Reminder system -β”‚ β”œβ”€β”€ fixtures.ts # Test fixtures -β”‚ └── helpers.ts # Test helpers -└── setup.ts # Global test setup -``` - -## Running Tests After Cleanup - -### All Tests -```bash -make test-all -``` - -### Specific Test Types -```bash -# Unit tests -make test - -# Integration tests -./tests/integration/run-integration.sh - -# E2E tests -make test-e2e -``` - -### Debugging -Instead of manual browser scripts, use: -```bash -# Interactive E2E debugging -make test-e2e-ui - -# Debug specific auth issues -bunx playwright test auth-debug.spec.ts --debug -``` - -## Migration Guide - -### Manual Tests β†’ E2E Tests -| Old Manual Script | New E2E Test | Purpose | -|---|---|---| -| `admin-login-debug.js` | `auth-debug.spec.ts` | Admin authentication validation | -| `auth-db-debug.js` | `auth-debug.spec.ts` | Database auth testing | -| `debug-email-validation.js` | `auth-debug.spec.ts` | Email format validation | - -### Benefits -- βœ… Automated instead of manual -- βœ… Cross-browser testing -- βœ… CI/CD integration -- βœ… Better error reporting -- βœ… Reproducible results -EOF - - echo -e "${GREEN} Created cleanup documentation${RESET}" -} - -# Function to create test optimization report -create_optimization_report() { - echo -e "${BLUE}πŸ“Š Creating optimization report...${RESET}" - - local report_file="$TESTS_DIR/cleanup-report.json" - - cat > "$report_file" << EOF -{ - "cleanup_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", - "removed_files": [ - "manual/admin-login-debug.js", - "manual/auth-db-debug.js", - "manual/debug-email-validation.js" - ], - "added_files": [ - "__mocks__/index.js", - "e2e/auth-debug.spec.ts", - "e2e/test-utils.ts", - "integration/run-integration.sh", - "README-CLEANUP.md" - ], - "optimizations": { - "removed_manual_tests": 3, - "added_automated_tests": 1, - "consolidated_mocks": true, - "added_test_utilities": true, - "improved_documentation": true - }, - "test_count_before": { - "manual": 3, - "e2e": 5, - "integration": 1, - "unit": "variable" - }, - "test_count_after": { - "manual": 0, - "e2e": 6, - "integration": 1, - "unit": "variable" - } -} -EOF - - echo -e "${GREEN} Created optimization report: $report_file${RESET}" -} - -# Function to validate test structure -validate_test_structure() { - echo -e "${BLUE}βœ… Validating test structure...${RESET}" - - local errors=0 - - # Check required directories exist - local required_dirs=( - "$TESTS_DIR" - "$TESTS_DIR/__mocks__" - "$TESTS_DIR/e2e" - "$TESTS_DIR/integration" - ) - - for dir in "${required_dirs[@]}"; do - if [ ! -d "$dir" ]; then - echo -e "${RED} ❌ Missing directory: $dir${RESET}" - ((errors++)) - else - echo -e "${GREEN} βœ… Directory exists: $(basename "$dir")${RESET}" - fi - done - - # Check critical test files exist - local required_files=( - "$TESTS_DIR/setup.ts" - "$TESTS_DIR/e2e/auth.spec.ts" - "$TESTS_DIR/integration/production.test.js" - ) - - for file in "${required_files[@]}"; do - if [ ! -f "$file" ]; then - echo -e "${RED} ❌ Missing file: $(basename "$file")${RESET}" - ((errors++)) - else - echo -e "${GREEN} βœ… File exists: $(basename "$file")${RESET}" - fi - done - - if [ $errors -eq 0 ]; then - echo -e "${GREEN}πŸŽ‰ Test structure validation passed!${RESET}" - return 0 - else - echo -e "${RED}πŸ’₯ Test structure validation failed with $errors errors${RESET}" - return 1 - fi -} - -# Main execution -main() { - echo -e "${BLUE}Starting test cleanup process...${RESET}" - - # Ensure we're in the right directory - if [ ! -d "$TESTS_DIR" ]; then - echo -e "${RED}❌ Tests directory not found: $TESTS_DIR${RESET}" - exit 1 - fi - - # Run cleanup steps - remove_redundant_files - consolidate_mocks - clean_e2e_tests - clean_integration_tests - update_test_docs - create_optimization_report - - # Validate the result - if validate_test_structure; then - echo -e "${GREEN}πŸŽ‰ Test cleanup completed successfully!${RESET}" - echo -e "${BLUE}πŸ“‹ Summary:${RESET}" - echo -e " β€’ Removed redundant manual test files" - echo -e " β€’ Consolidated mock utilities" - echo -e " β€’ Created optimized E2E test structure" - echo -e " β€’ Added automated test runners" - echo -e " β€’ Updated documentation" - echo -e "" - echo -e "${YELLOW}πŸ“– Next steps:${RESET}" - echo -e " 1. Review tests/README-CLEANUP.md" - echo -e " 2. Run 'make test-all' to verify tests work" - echo -e " 3. Update CI/CD to use new test structure" - echo -e " 4. Train team on new E2E debugging workflow" - else - echo -e "${RED}πŸ’₯ Test cleanup completed with errors${RESET}" - exit 1 - fi -} - -# Run the script -main "$@" diff --git a/scripts/deploy-k8s.sh b/scripts/deploy-k8s.sh deleted file mode 100755 index e3d28a8..0000000 --- a/scripts/deploy-k8s.sh +++ /dev/null @@ -1,393 +0,0 @@ -#!/bin/bash - -# Kubernetes deployment script with environment variable substitution -# This script processes template files and applies them to Kubernetes -# -# Registry Authentication Setup: -# To pull images from a private registry, set these environment variables: -# REGISTRY_USERNAME - Username for the container registry -# REGISTRY_PASSWORD - Password/token for the container registry -# REGISTRY_HOST - Registry hostname (default: gitea-http.taildb3494.ts.net) -# -# Example in .env file: -# REGISTRY_USERNAME=your-username -# REGISTRY_PASSWORD=your-password-or-token -# REGISTRY_HOST=gitea-http.taildb3494.ts.net - -set -euo pipefail - -# Color codes for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -K8S_DIR="$PROJECT_ROOT/k8s" -TEMP_DIR="/tmp/meds-k8s-deploy" - -# Function to print colored output -print_info() { - echo -e "${BLUE}ℹ️ $1${NC}" -} - -print_success() { - echo -e "${GREEN}βœ… $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠️ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -# Function to load environment variables -load_env() { - local env_file="$1" - if [[ -f "$env_file" ]]; then - print_info "Loading environment from $env_file" - # Export variables from .env file - set -a - source "$env_file" - set +a - else - print_warning "Environment file $env_file not found" - fi -} - -# Function to substitute environment variables in template files -substitute_templates() { - print_info "Processing template files..." - - # Create temporary directory - mkdir -p "$TEMP_DIR" - - # Process each template file - for template_file in "$K8S_DIR"/*.template; do - if [[ -f "$template_file" ]]; then - local filename=$(basename "$template_file" .template) - local output_file="$TEMP_DIR/$filename" - - print_info "Processing template: $filename" - - # Substitute environment variables - envsubst < "$template_file" > "$output_file" - - print_success "Generated: $output_file" - fi - done -} - -# Function to create registry authentication -create_registry_auth() { - if [[ -n "${REGISTRY_USERNAME:-}" && -n "${REGISTRY_PASSWORD:-}" ]]; then - local registry_host="${REGISTRY_HOST:-gitea-http.taildb3494.ts.net}" - local auth_string=$(echo -n "${REGISTRY_USERNAME}:${REGISTRY_PASSWORD}" | base64 -w 0) - local docker_config="{\"auths\":{\"${registry_host}\":{\"auth\":\"${auth_string}\"}}}" - export REGISTRY_AUTH_BASE64=$(echo -n "$docker_config" | base64 -w 0) - print_info "Registry authentication configured for $registry_host" - else - print_warning "Registry credentials not provided - skipping registry secret creation" - export REGISTRY_AUTH_BASE64="" - fi -} - -# Function to validate required environment variables -validate_env() { - local required_vars=("INGRESS_HOST") - local missing_vars=() - - for var in "${required_vars[@]}"; do - if [[ -z "${!var:-}" ]]; then - missing_vars+=("$var") - fi - done - - if [[ ${#missing_vars[@]} -gt 0 ]]; then - print_error "Missing required environment variables:" - for var in "${missing_vars[@]}"; do - echo " - $var" - done - echo "" - echo "Please set these variables in your .env file or environment." - exit 1 - fi -} - -# Function to ensure namespace exists -ensure_namespace() { - if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then - print_info "Creating namespace: $NAMESPACE" - kubectl create namespace "$NAMESPACE" - print_success "Created namespace: $NAMESPACE" - else - print_info "Using existing namespace: $NAMESPACE" - fi -} - -# Function to convert Kubernetes storage units to bytes -storage_to_bytes() { - local storage="$1" - local number=$(echo "$storage" | sed 's/[^0-9]*//g') - local unit=$(echo "$storage" | sed 's/[0-9]*//g') - - case "$unit" in - "Ki"|"K") echo $((number * 1024)) ;; - "Mi"|"M") echo $((number * 1024 * 1024)) ;; - "Gi"|"G") echo $((number * 1024 * 1024 * 1024)) ;; - "Ti"|"T") echo $((number * 1024 * 1024 * 1024 * 1024)) ;; - "") echo "$number" ;; - *) echo "0" ;; - esac -} - -# Function to check if PVC storage can be updated -can_update_pvc_storage() { - local pvc_file="$1" - local pvc_name=$(grep "name:" "$pvc_file" | head -1 | awk '{print $2}') - local new_storage=$(grep "storage:" "$pvc_file" | awk '{print $2}') - - # Check if PVC exists - if kubectl get pvc "$pvc_name" -n "$NAMESPACE" &> /dev/null; then - local current_storage=$(kubectl get pvc "$pvc_name" -n "$NAMESPACE" -o jsonpath='{.status.capacity.storage}') - - # Convert storage sizes to bytes for comparison - local current_bytes=$(storage_to_bytes "$current_storage") - local new_bytes=$(storage_to_bytes "$new_storage") - - if [[ "$new_bytes" -lt "$current_bytes" ]]; then - print_warning "Skipping PVC $pvc_name: cannot reduce storage from $current_storage to $new_storage" - return 1 - fi - fi - return 0 -} - -# Function to apply Kubernetes manifests -apply_manifests() { - local manifest_dir="$1" - - print_info "Applying Kubernetes manifests from $manifest_dir" - - # Apply static files that don't have template counterparts - for manifest_file in "$K8S_DIR"/*.yaml; do - if [[ -f "$manifest_file" && ! "$manifest_file" =~ \.template$ ]]; then - local basename_file=$(basename "$manifest_file") - local template_file="$K8S_DIR/${basename_file}.template" - - # Only apply if there's no corresponding template file - if [[ ! -f "$template_file" ]]; then - print_info "Applying static file: $basename_file" - kubectl apply -f "$manifest_file" -n "$NAMESPACE" - fi - fi - done - - # Apply processed template files - if [[ -d "$TEMP_DIR" ]]; then - for manifest_file in "$TEMP_DIR"/*.yaml; do - if [[ -f "$manifest_file" ]]; then - local basename_file=$(basename "$manifest_file") - - # Special handling for PVC files - if [[ "$basename_file" == *"pvc.yaml" ]] && grep -q "kind: PersistentVolumeClaim" "$manifest_file"; then - if can_update_pvc_storage "$manifest_file"; then - print_info "Applying template: $basename_file" - kubectl apply -f "$manifest_file" -n "$NAMESPACE" - fi - # Special handling for registry secret - skip if no auth provided - elif [[ "$basename_file" == *"registry-secret.yaml" ]] && [[ -z "${REGISTRY_AUTH_BASE64:-}" ]]; then - print_info "Skipping registry secret: no registry credentials provided" - else - print_info "Applying template: $basename_file" - if ! kubectl apply -f "$manifest_file" -n "$NAMESPACE" 2>/dev/null; then - # Handle StatefulSet update failures gracefully - if [[ "$basename_file" == *"statefulset.yaml" ]] && grep -q "kind: StatefulSet" "$manifest_file"; then - print_warning "StatefulSet update failed (likely due to immutable fields) - continuing deployment" - else - # Re-run the command to show the actual error for non-StatefulSet resources - kubectl apply -f "$manifest_file" -n "$NAMESPACE" - fi - fi - fi - fi - done - fi -} - -# Function to cleanup temporary files -cleanup() { - if [[ -d "$TEMP_DIR" ]]; then - print_info "Cleaning up temporary files..." - rm -rf "$TEMP_DIR" - fi -} - -# Function to show deployment status -show_status() { - print_info "Deployment Status:" - echo "" - - print_info "Pods:" - kubectl get pods -l app=rxminder -n "$NAMESPACE" - echo "" - - print_info "Services:" - kubectl get services -l app=rxminder -n "$NAMESPACE" - echo "" - - print_info "Ingress:" - kubectl get ingress -l app=rxminder -n "$NAMESPACE" - echo "" - - if [[ -n "${INGRESS_HOST:-}" ]]; then - print_success "Application should be available at: http://$INGRESS_HOST" - fi -} - -# Function to show usage -usage() { - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Options:" - echo " -e, --env FILE Environment file to load (default: .env)" - echo " -d, --dry-run Show what would be applied without applying" - echo " -s, --status Show deployment status only" - echo " -c, --cleanup Cleanup temporary files and exit" - echo " -h, --help Show this help message" - echo "" - echo "Examples:" - echo " $0 Deploy with default .env file" - echo " $0 -e .env.prod Deploy with production environment" - echo " $0 --dry-run Preview what would be deployed" - echo " $0 --status Check deployment status" -} - -# Main function -main() { - local env_file=".env" - local dry_run=false - local status_only=false - local cleanup_only=false - - # Parse command line arguments - while [[ $# -gt 0 ]]; do - case $1 in - -e|--env) - env_file="$2" - shift 2 - ;; - -d|--dry-run) - dry_run=true - shift - ;; - -s|--status) - status_only=true - shift - ;; - -c|--cleanup) - cleanup_only=true - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - print_error "Unknown option: $1" - usage - exit 1 - ;; - esac - done - - # Handle cleanup only - if [[ "$cleanup_only" == true ]]; then - cleanup - exit 0 - fi - - # Handle status only - if [[ "$status_only" == true ]]; then - load_env "$env_file" - NAMESPACE="${NAMESPACE:-rxminder}" - show_status - exit 0 - fi - - # Check if kubectl is available - if ! command -v kubectl &> /dev/null; then - print_error "kubectl is not installed or not in PATH" - exit 1 - fi - - # Check if we can connect to Kubernetes cluster - if ! kubectl cluster-info &> /dev/null; then - print_error "Cannot connect to Kubernetes cluster" - print_info "Make sure your kubectl is configured correctly" - exit 1 - fi - - print_info "πŸš€ Deploying Medication Reminder App to Kubernetes" - echo "" - - # Load environment variables - load_env "$env_file" - - # Set default values for required variables - export APP_NAME="${APP_NAME:-rxminder}" - export STORAGE_CLASS="${STORAGE_CLASS:-longhorn}" - export STORAGE_SIZE="${STORAGE_SIZE:-5Gi}" - export DOCKER_IMAGE="${DOCKER_IMAGE:-gitea-http.taildb3494.ts.net/will/rxminder:latest}" - - # Set default namespace if not provided in environment - NAMESPACE="${NAMESPACE:-rxminder}" - - # Create registry authentication if credentials are provided - create_registry_auth - - # Validate required environment variables - validate_env - - # Ensure namespace exists - ensure_namespace - - # Process templates - substitute_templates - - if [[ "$dry_run" == true ]]; then - print_info "Dry run mode - showing generated manifests:" - echo "" - - for manifest_file in "$TEMP_DIR"/*.yaml; do - if [[ -f "$manifest_file" ]]; then - echo "=== $(basename "$manifest_file") ===" - cat "$manifest_file" - echo "" - fi - done - else - # Apply manifests - apply_manifests "$K8S_DIR" - - print_success "Deployment completed!" - echo "" - - # Show status - show_status - fi - - # Cleanup - cleanup -} - -# Trap to ensure cleanup happens -trap cleanup EXIT - -# Run main function -main "$@" diff --git a/scripts/deploy-with-env.sh b/scripts/deploy-with-env.sh deleted file mode 100755 index c486a62..0000000 --- a/scripts/deploy-with-env.sh +++ /dev/null @@ -1,426 +0,0 @@ -#!/bin/bash - -# deploy-with-env.sh -# Deploy Kustomize configurations with environment variable substitution -# Usage: ./scripts/deploy-with-env.sh [environment] [action] -# Example: ./scripts/deploy-with-env.sh dev apply -# Example: ./scripts/deploy-with-env.sh prod diff - -set -euo pipefail - -# Default values -ENVIRONMENT=${1:-dev} -ACTION=${2:-apply} -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -# Color codes for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Load environment variables from multiple sources -load_environment() { - local env_files=( - "$HOME/.env" - "$PROJECT_ROOT/.env" - "$PROJECT_ROOT/.env.$ENVIRONMENT" - "$PROJECT_ROOT/.env.local" - ) - - print_status "Loading environment variables for: $ENVIRONMENT" - - for env_file in "${env_files[@]}"; do - if [[ -f "$env_file" ]]; then - print_status "Loading: $env_file" - set -a - source "$env_file" - set +a - fi - done - - # Set defaults if not provided - export APP_NAME="${APP_NAME:-rxminder}" - export NODE_ENV="${NODE_ENV:-$ENVIRONMENT}" - export IMAGE_TAG="${IMAGE_TAG:-latest}" - export NAMESPACE="${NAMESPACE:-${APP_NAME}-${ENVIRONMENT}}" - - # Environment-specific defaults - case "$ENVIRONMENT" in - "dev"|"development") - export NODE_ENV="development" - export LOG_LEVEL="${LOG_LEVEL:-debug}" - export DEBUG="${DEBUG:-true}" - export IMAGE_TAG="${IMAGE_TAG:-dev}" - export INGRESS_HOST="${INGRESS_HOST:-${APP_NAME}-dev.local}" - ;; - "prod"|"production") - export NODE_ENV="production" - export LOG_LEVEL="${LOG_LEVEL:-warn}" - export DEBUG="${DEBUG:-false}" - export IMAGE_TAG="${IMAGE_TAG:-v1.0.0}" - export INGRESS_HOST="${INGRESS_HOST:-${APP_NAME}.yourdomain.com}" - ;; - "staging") - export NODE_ENV="staging" - export LOG_LEVEL="${LOG_LEVEL:-info}" - export DEBUG="${DEBUG:-false}" - export IMAGE_TAG="${IMAGE_TAG:-staging}" - export INGRESS_HOST="${INGRESS_HOST:-staging.${APP_NAME}.yourdomain.com}" - ;; - esac - - print_success "Environment loaded: $ENVIRONMENT" - print_status "Key variables:" - echo " APP_NAME: $APP_NAME" - echo " NODE_ENV: $NODE_ENV" - echo " IMAGE_TAG: $IMAGE_TAG" - echo " NAMESPACE: $NAMESPACE" - echo " INGRESS_HOST: $INGRESS_HOST" -} - -# Substitute environment variables in kustomization files -substitute_variables() { - local overlay_dir="$PROJECT_ROOT/k8s-kustomize/overlays/$ENVIRONMENT" - local temp_dir=$(mktemp -d) - local kustomization_file="$overlay_dir/kustomization.yaml" - - print_status "Creating temporary overlay with variable substitution..." - - # Copy overlay directory to temp location - cp -r "$overlay_dir" "$temp_dir/" - local temp_overlay="$temp_dir/$(basename "$overlay_dir")" - - # Substitute variables in kustomization.yaml - if [[ -f "$temp_overlay/kustomization.yaml" ]]; then - envsubst < "$kustomization_file" > "$temp_overlay/kustomization.yaml.tmp" - mv "$temp_overlay/kustomization.yaml.tmp" "$temp_overlay/kustomization.yaml" - fi - - # Substitute variables in any other YAML files in the overlay - find "$temp_overlay" -name "*.yaml" -not -name "kustomization.yaml" | while read -r file; do - envsubst < "$file" > "$file.tmp" - mv "$file.tmp" "$file" - done - - echo "$temp_overlay" -} - -# Generate dynamic ConfigMap with current environment variables -generate_dynamic_config() { - local temp_dir="$1" - local config_file="$temp_dir/dynamic-config.env" - - print_status "Generating dynamic configuration..." - - cat > "$config_file" << EOF -# Dynamic configuration generated at deploy time -# Generated on: $(date) -# Environment: $ENVIRONMENT - -# Application Configuration -APP_NAME=$APP_NAME -NODE_ENV=$NODE_ENV -LOG_LEVEL=$LOG_LEVEL -DEBUG=$DEBUG - -# Image Configuration -IMAGE_TAG=$IMAGE_TAG -REGISTRY_URL=${REGISTRY_URL:-gitea-http.taildb3494.ts.net} -IMAGE_REPOSITORY=${IMAGE_REPOSITORY:-will/rxminder} - -# Network Configuration -INGRESS_HOST=$INGRESS_HOST -NAMESPACE=$NAMESPACE - -# API Configuration -REACT_APP_API_URL=${REACT_APP_API_URL:-http://${APP_NAME}-couchdb-service:5984} - -# Database Configuration -DB_HOST=${DB_HOST:-${APP_NAME}-couchdb-service} -DB_PORT=${DB_PORT:-5984} -COUCHDB_DATABASE_NAME=${COUCHDB_DATABASE_NAME:-meds_app} - -# Feature Flags -ENABLE_MONITORING=${ENABLE_MONITORING:-false} -ENABLE_METRICS=${ENABLE_METRICS:-false} -ENABLE_TRACING=${ENABLE_TRACING:-false} - -# Performance Configuration -CACHE_TTL=${CACHE_TTL:-1800} -REQUEST_TIMEOUT=${REQUEST_TIMEOUT:-30000} -MAX_CONNECTIONS=${MAX_CONNECTIONS:-100} - -# Security Configuration -ENABLE_CORS=${ENABLE_CORS:-true} -CORS_ORIGIN=${CORS_ORIGIN:-*} - -# Build Information -BUILD_VERSION=${BUILD_VERSION:-unknown} -BUILD_COMMIT=${BUILD_COMMIT:-$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")} -BUILD_DATE=${BUILD_DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)} -EOF - - # Update kustomization.yaml to include dynamic config - local kustomization_file="$temp_dir/kustomization.yaml" - - # Add configMapGenerator for dynamic config if not present - if ! grep -q "dynamic-config.env" "$kustomization_file" 2>/dev/null; then - cat >> "$kustomization_file" << EOF - -# Dynamic configuration added at deploy time -configMapGenerator: - - name: ${APP_NAME}-dynamic-config - envs: - - dynamic-config.env - behavior: create -EOF - fi - - print_success "Dynamic configuration generated" -} - -# Validate deployment prerequisites -validate_prerequisites() { - print_status "Validating prerequisites..." - - # Check kubectl - if ! command -v kubectl &> /dev/null; then - print_error "kubectl is not installed or not in PATH" - return 1 - fi - - # Check kubectl connectivity - if ! kubectl cluster-info &> /dev/null; then - print_error "Cannot connect to Kubernetes cluster" - return 1 - fi - - # Check if overlay exists - local overlay_dir="$PROJECT_ROOT/k8s-kustomize/overlays/$ENVIRONMENT" - if [[ ! -d "$overlay_dir" ]]; then - print_error "Overlay directory not found: $overlay_dir" - return 1 - fi - - # Check if kustomization.yaml exists - if [[ ! -f "$overlay_dir/kustomization.yaml" ]]; then - print_error "kustomization.yaml not found in: $overlay_dir" - return 1 - fi - - print_success "Prerequisites validated" -} - -# Execute kubectl command with the prepared overlay -execute_kubectl() { - local temp_overlay="$1" - local action="$2" - - print_status "Executing kubectl $action with prepared overlay..." - - case "$action" in - "apply") - kubectl apply -k "$temp_overlay" - ;; - "delete") - kubectl delete -k "$temp_overlay" --ignore-not-found=true - ;; - "diff") - kubectl diff -k "$temp_overlay" || true - ;; - "dry-run") - kubectl apply -k "$temp_overlay" --dry-run=client -o yaml - ;; - "validate") - kubectl kustomize "$temp_overlay" | kubectl apply --dry-run=client --validate=true -f - - ;; - "build") - kubectl kustomize "$temp_overlay" - ;; - *) - print_error "Unknown action: $action" - print_status "Supported actions: apply, delete, diff, dry-run, validate, build" - return 1 - ;; - esac -} - -# Show deployment status -show_status() { - local namespace="${NAMESPACE:-${APP_NAME}-${ENVIRONMENT}}" - - print_status "Deployment status for $ENVIRONMENT environment:" - - echo - echo "Namespace: $namespace" - kubectl get namespace "$namespace" 2>/dev/null || echo "Namespace not found" - - echo - echo "Deployments:" - kubectl get deployments -n "$namespace" -l app="$APP_NAME" 2>/dev/null || echo "No deployments found" - - echo - echo "Services:" - kubectl get services -n "$namespace" -l app="$APP_NAME" 2>/dev/null || echo "No services found" - - echo - echo "Ingress:" - kubectl get ingress -n "$namespace" -l app="$APP_NAME" 2>/dev/null || echo "No ingress found" - - echo - echo "ConfigMaps:" - kubectl get configmaps -n "$namespace" -l app="$APP_NAME" 2>/dev/null || echo "No configmaps found" -} - -# Cleanup function -cleanup() { - if [[ -n "${TEMP_DIR:-}" ]] && [[ -d "$TEMP_DIR" ]]; then - print_status "Cleaning up temporary files..." - rm -rf "$TEMP_DIR" - fi -} - -# Set up cleanup trap -trap cleanup EXIT - -# Show usage information -show_usage() { - cat << EOF -Usage: $0 [environment] [action] [options] - -ENVIRONMENTS: - dev, development Deploy to development environment - prod, production Deploy to production environment - staging Deploy to staging environment - -ACTIONS: - apply Apply the configuration (default) - delete Delete the deployment - diff Show differences - dry-run Show what would be applied - validate Validate configuration - build Build and show manifests - status Show deployment status - -OPTIONS: - -h, --help Show this help message - --no-env Skip loading environment files - --verbose Enable verbose output - -EXAMPLES: - $0 dev apply Deploy to development - $0 prod diff Show production differences - $0 staging delete Delete staging deployment - $0 dev status Show development status - -ENVIRONMENT FILES: - The script loads variables from (in order): - - ~/.env - - ./.env - - ./.env.\$ENVIRONMENT - - ./.env.local - -EOF -} - -# Main execution function -main() { - local skip_env=false - local verbose=false - - # Parse options - while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - show_usage - exit 0 - ;; - --no-env) - skip_env=true - shift - ;; - --verbose) - verbose=true - set -x - shift - ;; - -*) - print_error "Unknown option: $1" - show_usage - exit 1 - ;; - *) - if [[ -z "${ENVIRONMENT_SET:-}" ]]; then - ENVIRONMENT="$1" - ENVIRONMENT_SET=true - elif [[ -z "${ACTION_SET:-}" ]]; then - ACTION="$1" - ACTION_SET=true - else - print_error "Too many arguments: $1" - show_usage - exit 1 - fi - shift - ;; - esac - done - - print_status "Kustomize Deployment with Environment Variables" - print_status "Environment: $ENVIRONMENT" - print_status "Action: $ACTION" - - # Special case for status action - if [[ "$ACTION" == "status" ]]; then - if [[ "$skip_env" != "true" ]]; then - load_environment - fi - show_status - exit 0 - fi - - # Validate prerequisites - validate_prerequisites - - # Load environment variables - if [[ "$skip_env" != "true" ]]; then - load_environment - fi - - # Create temporary overlay with substituted variables - TEMP_DIR=$(substitute_variables) - generate_dynamic_config "$TEMP_DIR" - - # Execute the kubectl command - execute_kubectl "$TEMP_DIR" "$ACTION" - - if [[ "$ACTION" == "apply" ]]; then - print_success "Deployment completed successfully!" - show_status - elif [[ "$ACTION" == "delete" ]]; then - print_success "Resources deleted successfully!" - fi -} - -# Run main function with all arguments -main "$@" diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100755 index 31160a3..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,258 +0,0 @@ -#!/bin/bash - -# πŸ§ͺ Deployment Validation Script -# Validates complete deployment with all environment variables and health checks - -set -e - -echo "πŸš€ Starting deployment validation..." - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Cleanup function -cleanup() { - print_status "Cleaning up test containers..." - APP_NAME_LOWER=$(echo "${APP_NAME:-meds}" | tr '[:upper:]' '[:lower:]') - docker stop ${APP_NAME_LOWER}-validation-test 2>/dev/null || true - docker rm ${APP_NAME_LOWER}-validation-test 2>/dev/null || true - docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation down 2>/dev/null || true -} - -# Set trap for cleanup -trap cleanup EXIT - -print_status "1. Validating environment files..." - -# Check if required environment files exist -if [[ ! -f .env ]]; then - print_error ".env file not found. Run 'cp .env.example .env' and configure it." - exit 1 -fi - -if [[ ! -f .env.example ]]; then - print_error ".env.example file not found." - exit 1 -fi - -print_success "Environment files exist" - -# Validate environment consistency -print_status "2. Checking environment variable consistency..." -./scripts/validate-env.sh - -print_status "3. Setting up Docker Buildx..." - -# Ensure buildx is available -if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx." - exit 1 -fi - -# Create a new builder instance if it doesn't exist -if ! docker buildx ls | grep -q "meds-builder"; then - print_status "Creating new buildx builder instance..." - docker buildx create --name meds-builder --driver docker-container --bootstrap -fi - -# Use the builder -docker buildx use meds-builder - -print_status "4. Building multi-platform Docker image with buildx..." - -# Convert APP_NAME to lowercase for Docker compatibility -APP_NAME_LOWER=$(echo "${APP_NAME:-meds}" | tr '[:upper:]' '[:lower:]') - -# Determine host platform -HOST_ARCH="$(uname -m)" -case "$HOST_ARCH" in - x86_64) HOST_PLATFORM="linux/amd64" ;; - aarch64|arm64) HOST_PLATFORM="linux/arm64" ;; - *) HOST_PLATFORM="linux/amd64" ;; -esac - -# Decide build strategy: -# Enable multi-platform (push) if MULTI_PLATFORM=1 or CONTAINER_REGISTRY is set -if [[ "${MULTI_PLATFORM:-0}" == "1" || -n "${CONTAINER_REGISTRY:-}" ]]; then - BUILD_PLATFORMS="linux/amd64,linux/arm64" - EXPORT_MODE="--push" - # If CONTAINER_REGISTRY provided, ensure trailing slash - if [[ -n "${CONTAINER_REGISTRY:-}" && "${CONTAINER_REGISTRY}" != */ ]]; then - CONTAINER_REGISTRY="${CONTAINER_REGISTRY}/" - fi - IMAGE_TAG="${IMAGE_TAG:-${CONTAINER_REGISTRY:-}${APP_NAME_LOWER}-validation:latest}" - print_status "Multi-platform build enabled (platforms: $BUILD_PLATFORMS) -> push $IMAGE_TAG" -else - BUILD_PLATFORMS="$HOST_PLATFORM" - EXPORT_MODE="--load" - IMAGE_TAG="${IMAGE_TAG:-${APP_NAME_LOWER}-validation}" - print_status "Single-platform build ($BUILD_PLATFORMS) -> load locally as $IMAGE_TAG" -fi - -# Perform build -docker buildx build --no-cache \ ---platform "$BUILD_PLATFORMS" \ ---build-arg APP_NAME="${APP_NAME:-RxMinder}" \ ---build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \ ---build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \ ---build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \ ---build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \ ---build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \ ---build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \ ---build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \ ---build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \ ---build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \ ---build-arg NODE_ENV="${NODE_ENV:-production}" \ --t "$IMAGE_TAG" \ -$EXPORT_MODE \ -. - -# If we pushed (multi-platform), pull the host-specific image for local tests -if [[ "$EXPORT_MODE" == "--push" ]]; then - print_status "Pulling image $IMAGE_TAG for local validation..." - docker pull "$IMAGE_TAG" -fi - -print_success "Docker image built successfully" - -print_status "5. Testing container startup and health..." - -# Run container in background -docker run --rm -d \ --p 8083:80 \ ---name ${APP_NAME_LOWER}-validation-test \ -"$IMAGE_TAG" - -# Wait for container to start -sleep 5 - -# Check if container is running -if ! docker ps | grep -q ${APP_NAME_LOWER}-validation-test; then - print_error "Container failed to start" - docker logs ${APP_NAME_LOWER}-validation-test - exit 1 -fi - -print_success "Container started successfully" - -# Test health endpoint -print_status "5. Testing health endpoint..." -for i in {1..10}; do - if curl -s -f http://localhost:8083/health > /dev/null; then - print_success "Health endpoint responding" - break - elif [[ $i -eq 10 ]]; then - print_error "Health endpoint not responding after 10 attempts" - exit 1 - else - print_warning "Health endpoint not ready, retrying... ($i/10)" - sleep 2 - fi -done - -# Test main application -print_status "6. Testing main application..." -HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083) -if [[ $HTTP_CODE -eq 200 ]]; then - print_success "Main application responding (HTTP $HTTP_CODE)" -else - print_error "Main application not responding properly (HTTP $HTTP_CODE)" - exit 1 -fi - -# Test docker-compose build -print_status "7. Testing Docker Compose build..." -docker compose -f docker/docker-compose.yaml build frontend --no-cache - -print_success "Docker Compose build successful" - -# Test docker-compose with validation project name -print_status "8. Testing Docker Compose deployment..." -docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation up -d --build - -# Wait for services to start -sleep 10 - -# Check service health -if docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation ps | grep -q "Up"; then - print_success "Docker Compose services started successfully" -else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation logs - exit 1 -fi - -# Test health of compose deployment -if curl -s -f http://localhost:8080/health > /dev/null; then - print_success "Docker Compose health endpoint responding" -else - print_warning "Docker Compose health endpoint not responding (may need CouchDB)" -fi - -print_status "9. Checking image size..." -IMAGE_SIZE=$(docker image inspect "$IMAGE_TAG" --format='{{.Size}}' | numfmt --to=iec) -print_success "Image size: $IMAGE_SIZE" - -print_status "10. Validating security configuration..." - -# Check if image runs as non-root -USER_INFO=$(docker run --rm "$IMAGE_TAG" whoami) -if [[ "$USER_INFO" != "root" ]]; then - print_success "Container runs as non-root user: $USER_INFO" -else - print_warning "Container runs as root user (security consideration)" -fi - -# Check nginx configuration -if docker run --rm "$IMAGE_TAG" nginx -t 2>/dev/null; then - print_success "Nginx configuration is valid" -else - print_error "Nginx configuration has issues" - exit 1 -fi - -print_status "11. Final validation complete!" - -echo -echo "πŸŽ‰ Deployment validation successful!" -echo -echo "Summary:" -echo "βœ… Environment files validated" -echo "βœ… Docker image builds successfully" -echo "βœ… Container starts and runs healthy" -echo "βœ… Health endpoints respond correctly" -echo "βœ… Docker Compose deployment works" -echo "βœ… Security configuration validated" -echo "βœ… Image size optimized ($IMAGE_SIZE)" -echo -echo "Your deployment is ready for production! πŸš€" -echo -echo "Next steps:" -echo "1. Configure production environment variables in .env" -echo "2. Run './deploy.sh production' for production deployment" -echo "3. Set up monitoring and backups" -echo "4. Configure SSL/TLS certificates" -echo diff --git a/scripts/generate-config.sh b/scripts/generate-config.sh deleted file mode 100755 index 29fb5e4..0000000 --- a/scripts/generate-config.sh +++ /dev/null @@ -1,474 +0,0 @@ -#!/bin/bash - -# generate-config.sh -# Generates config.env files for Kustomize from environment variables -# Usage: ./scripts/generate-config.sh [environment] -# Example: ./scripts/generate-config.sh dev - -set -euo pipefail - -# Default environment -ENVIRONMENT=${1:-dev} -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -# Color codes for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Load environment variables from various sources -load_env_files() { - local env_files=( - "$HOME/.env" - "$PROJECT_ROOT/.env" - "$PROJECT_ROOT/.env.$ENVIRONMENT" - "$PROJECT_ROOT/.env.local" - ) - - print_status "Loading environment variables..." - - for env_file in "${env_files[@]}"; do - if [[ -f "$env_file" ]]; then - print_status "Loading: $env_file" - # Source the file in a subshell to avoid polluting current environment - set -a - source "$env_file" - set +a - else - print_warning "File not found: $env_file" - fi - done -} - -# Generate config.env for base configuration -generate_base_config() { - local config_file="$PROJECT_ROOT/k8s-kustomize/base/config.env" - local temp_file=$(mktemp) - - print_status "Generating base config.env..." - - cat > "$temp_file" << EOF -# Base configuration for rxminder application -# Generated automatically from environment variables -# Generated on: $(date) -# Environment: $ENVIRONMENT - -# Application Environment -NODE_ENV=${NODE_ENV:-production} -LOG_LEVEL=${LOG_LEVEL:-info} - -# API Configuration -REACT_APP_API_URL=${REACT_APP_API_URL:-http://rxminder-couchdb-service:5984} - -# Feature Flags -ENABLE_MONITORING=${ENABLE_MONITORING:-false} -DEBUG=${DEBUG:-false} - -# Cache Configuration -CACHE_TTL=${CACHE_TTL:-1800} - -# Database Configuration -DB_HOST=${DB_HOST:-rxminder-couchdb-service} -DB_PORT=${DB_PORT:-5984} -COUCHDB_DATABASE_NAME=${COUCHDB_DATABASE_NAME:-meds_app} - -# Security Configuration -ENABLE_CORS=${ENABLE_CORS:-true} -CORS_ORIGIN=${CORS_ORIGIN:-*} - -# Performance Configuration -REQUEST_TIMEOUT=${REQUEST_TIMEOUT:-30000} -MAX_CONNECTIONS=${MAX_CONNECTIONS:-100} - -# Logging Configuration -LOG_FORMAT=${LOG_FORMAT:-json} -LOG_TIMESTAMP=${LOG_TIMESTAMP:-true} - -# Health Check Configuration -HEALTH_CHECK_INTERVAL=${HEALTH_CHECK_INTERVAL:-30} -READINESS_CHECK_TIMEOUT=${READINESS_CHECK_TIMEOUT:-5} - -# Application Metadata -APP_NAME=${APP_NAME:-rxminder} -APP_VERSION=${APP_VERSION:-1.0.0} - -# Container Registry -REGISTRY_URL=${REGISTRY_URL:-gitea-http.taildb3494.ts.net} -IMAGE_REPOSITORY=${IMAGE_REPOSITORY:-will/rxminder} - -# Ingress Configuration -INGRESS_CLASS=${INGRESS_CLASS:-nginx} -CERT_MANAGER_ISSUER=${CERT_MANAGER_ISSUER:-letsencrypt-prod} - -# Monitoring and Observability -ENABLE_METRICS=${ENABLE_METRICS:-false} -METRICS_PORT=${METRICS_PORT:-9090} -ENABLE_TRACING=${ENABLE_TRACING:-false} - -# Development specific (will be overridden in overlays) -DEV_MODE=${DEV_MODE:-false} -HOT_RELOAD=${HOT_RELOAD:-false} -EOF - - # Move temp file to final location - mv "$temp_file" "$config_file" - print_success "Generated: $config_file" -} - -# Generate environment-specific config -generate_environment_config() { - local overlay_dir="$PROJECT_ROOT/k8s-kustomize/overlays/$ENVIRONMENT" - local env_config_file="$overlay_dir/config.env" - - if [[ ! -d "$overlay_dir" ]]; then - print_error "Environment overlay directory not found: $overlay_dir" - return 1 - fi - - print_status "Generating environment-specific config for: $ENVIRONMENT" - - case "$ENVIRONMENT" in - "dev"|"development") - generate_dev_config "$env_config_file" - ;; - "prod"|"production") - generate_prod_config "$env_config_file" - ;; - "staging") - generate_staging_config "$env_config_file" - ;; - *) - print_warning "Unknown environment: $ENVIRONMENT. Generating generic config." - generate_generic_config "$env_config_file" - ;; - esac -} - -# Generate development-specific configuration -generate_dev_config() { - local config_file="$1" - local temp_file=$(mktemp) - - cat > "$temp_file" << EOF -# Development environment configuration -# Generated on: $(date) - -NODE_ENV=development -LOG_LEVEL=debug -DEBUG=true -ENABLE_MONITORING=false -DEV_MODE=true -HOT_RELOAD=true - -# Development URLs (override if needed) -REACT_APP_API_URL=${DEV_API_URL:-http://rxminder-couchdb-service:5984} -CORS_ORIGIN=${DEV_CORS_ORIGIN:-*} - -# Development domain -INGRESS_HOST=${DEV_INGRESS_HOST:-rxminder-dev.local} - -# Relaxed timeouts for debugging -REQUEST_TIMEOUT=60000 -HEALTH_CHECK_INTERVAL=60 - -# Development image tag -IMAGE_TAG=${DEV_IMAGE_TAG:-dev} -EOF - - mv "$temp_file" "$config_file" - print_success "Generated development config: $config_file" -} - -# Generate production-specific configuration -generate_prod_config() { - local config_file="$1" - local temp_file=$(mktemp) - - cat > "$temp_file" << EOF -# Production environment configuration -# Generated on: $(date) - -NODE_ENV=production -LOG_LEVEL=${PROD_LOG_LEVEL:-warn} -DEBUG=false -ENABLE_MONITORING=true -DEV_MODE=false - -# Production URLs -REACT_APP_API_URL=${PROD_API_URL:-http://rxminder-couchdb-service:5984} -CORS_ORIGIN=${PROD_CORS_ORIGIN:-https://rxminder.yourdomain.com} - -# Production domain -INGRESS_HOST=${PROD_INGRESS_HOST:-rxminder.yourdomain.com} - -# Production performance settings -CACHE_TTL=3600 -REQUEST_TIMEOUT=30000 -MAX_CONNECTIONS=200 - -# Production monitoring -ENABLE_METRICS=true -ENABLE_TRACING=true - -# Production image tag -IMAGE_TAG=${PROD_IMAGE_TAG:-v1.0.0} - -# Security settings -ENABLE_SECURITY_HEADERS=true -ENABLE_RATE_LIMITING=true -EOF - - mv "$temp_file" "$config_file" - print_success "Generated production config: $config_file" -} - -# Generate staging-specific configuration -generate_staging_config() { - local config_file="$1" - local temp_file=$(mktemp) - - cat > "$temp_file" << EOF -# Staging environment configuration -# Generated on: $(date) - -NODE_ENV=staging -LOG_LEVEL=${STAGING_LOG_LEVEL:-info} -DEBUG=false -ENABLE_MONITORING=true -DEV_MODE=false - -# Staging URLs -REACT_APP_API_URL=${STAGING_API_URL:-http://rxminder-couchdb-service:5984} -CORS_ORIGIN=${STAGING_CORS_ORIGIN:-https://staging.rxminder.yourdomain.com} - -# Staging domain -INGRESS_HOST=${STAGING_INGRESS_HOST:-staging.rxminder.yourdomain.com} - -# Staging image tag -IMAGE_TAG=${STAGING_IMAGE_TAG:-staging} - -# Enable monitoring but with relaxed settings -ENABLE_METRICS=true -ENABLE_TRACING=false -EOF - - mv "$temp_file" "$config_file" - print_success "Generated staging config: $config_file" -} - -# Generate generic environment configuration -generate_generic_config() { - local config_file="$1" - local temp_file=$(mktemp) - - cat > "$temp_file" << EOF -# Generic environment configuration for: $ENVIRONMENT -# Generated on: $(date) - -NODE_ENV=${ENVIRONMENT} -LOG_LEVEL=${LOG_LEVEL:-info} -DEBUG=${DEBUG:-false} - -# Image tag for this environment -IMAGE_TAG=${ENVIRONMENT} -EOF - - mv "$temp_file" "$config_file" - print_success "Generated generic config: $config_file" -} - -# Generate secrets template (not actual secrets) -generate_secrets_template() { - local secrets_file="$PROJECT_ROOT/k8s-kustomize/overlays/$ENVIRONMENT/secrets.env.template" - local temp_file=$(mktemp) - - print_status "Generating secrets template..." - - cat > "$temp_file" << EOF -# Secrets template for $ENVIRONMENT environment -# Copy this to secrets.env and fill in actual values -# DO NOT commit secrets.env to version control - -# Database credentials -COUCHDB_USERNAME=${COUCHDB_USERNAME:-admin} -COUCHDB_PASSWORD=CHANGE_ME_IN_${ENVIRONMENT^^} - -# Registry credentials (if using private registry) -REGISTRY_USERNAME=${REGISTRY_USERNAME:-} -REGISTRY_PASSWORD=CHANGE_ME -REGISTRY_EMAIL=${REGISTRY_EMAIL:-} - -# TLS/SSL certificates (base64 encoded) -TLS_CERT=CHANGE_ME -TLS_KEY=CHANGE_ME - -# API keys and tokens -API_SECRET_KEY=CHANGE_ME -JWT_SECRET=CHANGE_ME - -# External service credentials -MONITORING_API_KEY=CHANGE_ME -SMTP_PASSWORD=CHANGE_ME -EOF - - mv "$temp_file" "$secrets_file" - print_success "Generated secrets template: $secrets_file" - print_warning "Remember to create actual secrets.env file and add it to .gitignore!" -} - -# Validate generated configuration -validate_config() { - local config_file="$PROJECT_ROOT/k8s-kustomize/base/config.env" - - print_status "Validating generated configuration..." - - if [[ ! -f "$config_file" ]]; then - print_error "Config file not found: $config_file" - return 1 - fi - - # Check for required variables - local required_vars=("APP_NAME" "NODE_ENV" "REACT_APP_API_URL") - local missing_vars=() - - for var in "${required_vars[@]}"; do - if ! grep -q "^${var}=" "$config_file"; then - missing_vars+=("$var") - fi - done - - if [[ ${#missing_vars[@]} -gt 0 ]]; then - print_error "Missing required variables: ${missing_vars[*]}" - return 1 - fi - - print_success "Configuration validation passed!" -} - -# Display usage information -show_usage() { - cat << EOF -Usage: $0 [environment] [options] - -ENVIRONMENTS: - dev, development Generate development configuration - prod, production Generate production configuration - staging Generate staging configuration - Generate configuration for custom environment - -OPTIONS: - -h, --help Show this help message - -v, --validate Only validate existing configuration - --secrets Generate secrets template - --dry-run Show what would be generated without writing files - -EXAMPLES: - $0 dev Generate development configuration - $0 prod --secrets Generate production config and secrets template - $0 --validate Validate existing configuration - -ENVIRONMENT VARIABLES: - The script will load variables from: - - ~/.env (global user environment) - - ./.env (project environment) - - ./.env.\$ENVIRONMENT (environment-specific) - - ./.env.local (local overrides) - -EOF -} - -# Main execution -main() { - local validate_only=false - local generate_secrets=false - local dry_run=false - - # Parse command line arguments - while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - show_usage - exit 0 - ;; - -v|--validate) - validate_only=true - shift - ;; - --secrets) - generate_secrets=true - shift - ;; - --dry-run) - dry_run=true - shift - ;; - -*) - print_error "Unknown option: $1" - show_usage - exit 1 - ;; - *) - ENVIRONMENT="$1" - shift - ;; - esac - done - - print_status "Kustomize Config Generator" - print_status "Environment: $ENVIRONMENT" - - if [[ "$validate_only" == "true" ]]; then - validate_config - exit $? - fi - - if [[ "$dry_run" == "true" ]]; then - print_status "DRY RUN MODE - No files will be written" - # Set dry run flag for other functions to check - export DRY_RUN=true - fi - - # Load environment variables - load_env_files - - # Generate configurations - generate_base_config - generate_environment_config - - if [[ "$generate_secrets" == "true" ]]; then - generate_secrets_template - fi - - # Validate generated configuration - validate_config - - print_success "Configuration generation completed!" - print_status "Next steps:" - echo " 1. Review generated files in k8s-kustomize/" - echo " 2. Update any environment-specific values" - echo " 3. Create secrets.env files for sensitive data" - echo " 4. Test with: make kustomize-dry-run-$ENVIRONMENT" -} - -# Run main function with all arguments -main "$@" diff --git a/scripts/generate-unified-config.ts b/scripts/generate-unified-config.ts deleted file mode 100644 index 5f72b2a..0000000 --- a/scripts/generate-unified-config.ts +++ /dev/null @@ -1,612 +0,0 @@ -#!/usr/bin/env bun - -/** - * Generate Configuration Files from Unified Config - * - * This script reads the unified configuration and generates all necessary - * config files for different environments and deployment targets. - * - * Usage: - * bun scripts/generate-unified-config.ts [environment] - * bun scripts/generate-unified-config.ts production - * bun scripts/generate-unified-config.ts --all - */ - -import { writeFileSync, mkdirSync, existsSync } from 'fs'; -import { join, dirname } from 'path'; -import { - createUnifiedConfigForEnvironment, - exportAsEnvVars, - type Environment, - type UnifiedConfig, -} from '../config/unified.config'; - -interface GeneratorOptions { - environment?: Environment; - outputDir?: string; - generateAll?: boolean; - dryRun?: boolean; - verbose?: boolean; -} - -class ConfigGenerator { - private options: GeneratorOptions; - private projectRoot: string; - - constructor(options: GeneratorOptions = {}) { - this.options = options; - this.projectRoot = join(__dirname, '..'); - } - - /** - * Generate all configuration files - */ - async generate(): Promise { - const environments: Environment[] = this.options.generateAll - ? ['development', 'staging', 'production'] - : [this.options.environment || this.getCurrentEnvironment()]; - - console.warn('πŸ”§ Generating unified configuration files...'); - console.warn(`πŸ“ Project root: ${this.projectRoot}`); - console.warn(`🌍 Environments: ${environments.join(', ')}`); - - for (const env of environments) { - await this.generateForEnvironment(env); - } - - console.warn('βœ… Configuration generation completed!'); - } - - /** - * Generate configuration files for a specific environment - */ - private async generateForEnvironment( - environment: Environment - ): Promise { - console.warn(`\nπŸ”„ Generating configuration for: ${environment}`); - - // Create config instance for specific environment - const config = createUnifiedConfigForEnvironment(environment); - const envVars = exportAsEnvVars(config); - - // Generate different config file formats - await Promise.all([ - this.generateDotEnv(environment, envVars), - this.generateKubernetesConfig(environment, config, envVars), - this.generateDockerEnv(environment, envVars), - this.generateViteEnv(environment, envVars), - this.generateTypeScriptConfig(environment, config), - ]); - - console.warn(`βœ… Generated configuration for ${environment}`); - } - - /** - * Generate .env file - */ - private async generateDotEnv( - environment: Environment, - envVars: Record - ): Promise { - const content = this.createEnvFileContent(environment, envVars); - const filePath = join(this.projectRoot, `.env.${environment}`); - - this.writeFile(filePath, content, `πŸ“„ .env.${environment}`); - } - - /** - * Generate Kubernetes config.env file - */ - private async generateKubernetesConfig( - environment: Environment, - config: UnifiedConfig, - envVars: Record - ): Promise { - const overlayDir = join( - this.projectRoot, - 'k8s-kustomize', - 'overlays', - environment - ); - this.ensureDirectoryExists(overlayDir); - - // Generate config.env for kustomize - const configContent = this.createKubernetesConfigContent( - environment, - envVars - ); - const configPath = join(overlayDir, 'config.env'); - this.writeFile( - configPath, - configContent, - `🚒 Kubernetes config.env (${environment})` - ); - - // Generate kustomization.yaml updates - await this.updateKustomizationFile(environment, config); - } - - /** - * Generate Docker environment file - */ - private async generateDockerEnv( - environment: Environment, - envVars: Record - ): Promise { - const dockerDir = join(this.projectRoot, 'docker'); - this.ensureDirectoryExists(dockerDir); - - const content = this.createDockerEnvContent(environment, envVars); - const filePath = join(dockerDir, `.env.${environment}`); - - this.writeFile(filePath, content, `🐳 Docker .env.${environment}`); - } - - /** - * Generate Vite-specific environment file - */ - private async generateViteEnv( - environment: Environment, - envVars: Record - ): Promise { - const viteVars = Object.entries(envVars) - .filter(([key]) => key.startsWith('VITE_') || this.isViteRelevant(key)) - .reduce( - (acc, [key, value]) => { - acc[key.startsWith('VITE_') ? key : `VITE_${key}`] = value; - return acc; - }, - {} as Record - ); - - const content = this.createViteEnvContent(environment, viteVars); - const filePath = join(this.projectRoot, `.env.vite.${environment}`); - - this.writeFile(filePath, content, `⚑ Vite .env.vite.${environment}`); - } - - /** - * Generate TypeScript config export - */ - private async generateTypeScriptConfig( - environment: Environment, - config: UnifiedConfig - ): Promise { - const configDir = join(this.projectRoot, 'config', 'generated'); - this.ensureDirectoryExists(configDir); - - const content = this.createTypeScriptConfigContent(environment, config); - const filePath = join(configDir, `${environment}.config.ts`); - - this.writeFile(filePath, content, `πŸ“ TypeScript ${environment}.config.ts`); - } - - /** - * Create .env file content - */ - private createEnvFileContent( - environment: Environment, - envVars: Record - ): string { - const timestamp = new Date().toISOString(); - - return `# ${environment.toUpperCase()} Environment Configuration -# Generated automatically from unified configuration -# Generated on: ${timestamp} -# -# This file was auto-generated. Do not edit manually. -# To make changes, update config/unified.config.ts and regenerate. - -${Object.entries(envVars) - .map(([key, value]) => `${key}=${value}`) - .join('\n')} - -# Additional environment-specific variables can be added below -# (These will not be overwritten during regeneration) - -`; - } - - /** - * Create Kubernetes config.env content - */ - private createKubernetesConfigContent( - environment: Environment, - envVars: Record - ): string { - const timestamp = new Date().toISOString(); - - // Filter to Kubernetes-relevant variables - const k8sVars = Object.entries(envVars) - .filter( - ([key]) => !key.startsWith('VITE_') && this.isKubernetesRelevant(key) - ) - .reduce( - (acc, [key, value]) => { - acc[key] = value; - return acc; - }, - {} as Record - ); - - return `# Kubernetes configuration for ${environment} -# Generated automatically from unified configuration -# Generated on: ${timestamp} - -${Object.entries(k8sVars) - .map(([key, value]) => `${key}=${value}`) - .join('\n')} -`; - } - - /** - * Create Docker environment content - */ - private createDockerEnvContent( - environment: Environment, - envVars: Record - ): string { - const timestamp = new Date().toISOString(); - - return `# Docker environment for ${environment} -# Generated on: ${timestamp} - -# Container Configuration -DOCKER_IMAGE=${envVars.DOCKER_IMAGE} -CONTAINER_REGISTRY=${envVars.CONTAINER_REGISTRY} -CONTAINER_REPOSITORY=${envVars.CONTAINER_REPOSITORY} -CONTAINER_TAG=${envVars.CONTAINER_TAG} - -# Application Configuration -APP_NAME=${envVars.APP_NAME} -NODE_ENV=${envVars.NODE_ENV} -PORT=${envVars.PORT} - -# Database Configuration -COUCHDB_URL=${envVars.COUCHDB_URL} -COUCHDB_USER=${envVars.COUCHDB_USER} -COUCHDB_PASSWORD=${envVars.COUCHDB_PASSWORD} -`; - } - - /** - * Create Vite environment content - */ - private createViteEnvContent( - environment: Environment, - viteVars: Record - ): string { - const timestamp = new Date().toISOString(); - - return `# Vite environment variables for ${environment} -# Generated on: ${timestamp} -# -# These variables are available in the frontend build process - -${Object.entries(viteVars) - .map(([key, value]) => `${key}=${value}`) - .join('\n')} -`; - } - - /** - * Create TypeScript config content - */ - private createTypeScriptConfigContent( - environment: Environment, - config: UnifiedConfig - ): string { - const timestamp = new Date().toISOString(); - - return `/** - * Generated configuration for ${environment} - * Generated on: ${timestamp} - * - * This file exports the resolved configuration for the ${environment} environment. - * It can be imported by other TypeScript files for type-safe configuration access. - */ - -import type { UnifiedConfig } from '../unified.config'; - -export const ${environment}Config: UnifiedConfig = ${JSON.stringify(config, null, 2)} as const; - -export default ${environment}Config; -`; - } - - /** - * Update kustomization.yaml file with current configuration - */ - private async updateKustomizationFile( - environment: Environment, - config: UnifiedConfig - ): Promise { - const overlayDir = join( - this.projectRoot, - 'k8s-kustomize', - 'overlays', - environment - ); - const kustomizationPath = join(overlayDir, 'kustomization.yaml'); - - if (!existsSync(kustomizationPath)) { - // Create new kustomization.yaml - const content = this.createKustomizationContent(environment, config); - this.writeFile( - kustomizationPath, - content, - `πŸŽ›οΈ kustomization.yaml (${environment})` - ); - } else { - console.warn( - ` ℹ️ Kustomization file exists: ${kustomizationPath} (not overwriting)` - ); - } - } - - /** - * Create kustomization.yaml content - */ - private createKustomizationContent( - environment: Environment, - config: UnifiedConfig - ): string { - return `apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -metadata: - name: ${config.app.name}-${environment} - -# Reference the base configuration -resources: - - ../../base - - namespace.yaml - -# Override namespace for ${environment} -namespace: ${config.kubernetes.namespace} - -# ${environment.charAt(0).toUpperCase() + environment.slice(1)}-specific labels -labels: - - pairs: - environment: ${environment} - tier: ${environment === 'production' ? 'prod' : environment} - -# ${environment.charAt(0).toUpperCase() + environment.slice(1)} image tags and configurations -images: - - name: frontend-image - newName: ${config.container.registry}/${config.container.repository} - newTag: ${config.container.tag} - - name: couchdb-image - newName: couchdb - newTag: 3.3.2 - -# ${environment.charAt(0).toUpperCase() + environment.slice(1)} replicas -replicas: - - name: ${config.app.name}-frontend - count: ${config.kubernetes.replicas.frontend} - - name: ${config.app.name}-couchdb - count: ${config.kubernetes.replicas.database} - -# Environment-specific patches -patches: - # Resource limits - - target: - kind: Deployment - name: ${config.app.name}-frontend - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "${config.kubernetes.resources.frontend.requests.memory}" - cpu: "${config.kubernetes.resources.frontend.requests.cpu}" - limits: - memory: "${config.kubernetes.resources.frontend.limits.memory}" - cpu: "${config.kubernetes.resources.frontend.limits.cpu}" - - - target: - kind: StatefulSet - name: ${config.app.name}-couchdb - patch: |- - - op: replace - path: /spec/template/spec/containers/0/resources - value: - requests: - memory: "${config.kubernetes.resources.database.requests.memory}" - cpu: "${config.kubernetes.resources.database.requests.cpu}" - limits: - memory: "${config.kubernetes.resources.database.limits.memory}" - cpu: "${config.kubernetes.resources.database.limits.cpu}" - -# ConfigMap generation -configMapGenerator: - - name: ${config.app.name}-config - envs: - - config.env - behavior: create -`; - } - - /** - * Check if a variable is relevant for Vite - */ - private isViteRelevant(key: string): boolean { - const viteRelevantKeys = [ - 'APP_NAME', - 'APP_VERSION', - 'APP_BASE_URL', - 'COUCHDB_URL', - 'COUCHDB_USER', - 'COUCHDB_PASSWORD', - 'MAILGUN_API_KEY', - 'MAILGUN_DOMAIN', - 'MAILGUN_FROM_NAME', - 'MAILGUN_FROM_EMAIL', - 'GOOGLE_CLIENT_ID', - 'GITHUB_CLIENT_ID', - 'ENABLE_EMAIL_VERIFICATION', - 'ENABLE_OAUTH', - 'ENABLE_ADMIN_INTERFACE', - 'DEBUG_MODE', - ]; - return viteRelevantKeys.includes(key); - } - - /** - * Check if a variable is relevant for Kubernetes - */ - private isKubernetesRelevant(key: string): boolean { - const k8sIrrelevantKeys = ['VITE_', 'CONTAINER_', 'DOCKER_']; - return !k8sIrrelevantKeys.some(prefix => key.startsWith(prefix)); - } - - /** - * Get current environment from NODE_ENV - */ - private getCurrentEnvironment(): Environment { - const env = process.env.NODE_ENV || 'development'; - return ['development', 'staging', 'production', 'test'].includes(env) - ? (env as Environment) - : 'development'; - } - - /** - * Ensure directory exists - */ - private ensureDirectoryExists(dirPath: string): void { - if (!existsSync(dirPath)) { - mkdirSync(dirPath, { recursive: true }); - if (this.options.verbose) { - console.warn(` πŸ“ Created directory: ${dirPath}`); - } - } - } - - /** - * Write file with logging - */ - private writeFile( - filePath: string, - content: string, - description: string - ): void { - if (this.options.dryRun) { - console.warn(` πŸ” [DRY RUN] Would write: ${description}`); - console.warn(` Path: ${filePath}`); - return; - } - - // Ensure directory exists - this.ensureDirectoryExists(dirname(filePath)); - - writeFileSync(filePath, content, 'utf8'); - console.warn(` βœ… Generated: ${description}`); - - if (this.options.verbose) { - console.warn(` Path: ${filePath}`); - console.warn(` Size: ${content.length} bytes`); - } - } -} - -/** - * CLI Interface - */ -async function main() { - const args = process.argv.slice(2); - const options: GeneratorOptions = {}; - - // Parse command line arguments - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - switch (arg) { - case '--all': - options.generateAll = true; - break; - case '--dry-run': - options.dryRun = true; - break; - case '--verbose': - case '-v': - options.verbose = true; - break; - case '--help': - case '-h': - showHelp(); - process.exit(0); - break; - default: - if (!arg.startsWith('--') && !options.environment) { - options.environment = arg as Environment; - } - break; - } - } - - // Validate environment - if ( - options.environment && - !['development', 'staging', 'production', 'test'].includes( - options.environment - ) - ) { - console.error(`❌ Invalid environment: ${options.environment}`); - console.error( - ' Valid environments: development, staging, production, test' - ); - process.exit(1); - } - - try { - const generator = new ConfigGenerator(options); - await generator.generate(); - } catch (error) { - console.error('❌ Configuration generation failed:', error); - process.exit(1); - } -} - -/** - * Show help message - */ -function showHelp() { - console.warn(` -πŸ”§ Unified Configuration Generator - -USAGE: - bun scripts/generate-unified-config.ts [environment] [options] - -ARGUMENTS: - environment Target environment (development, staging, production, test) - -OPTIONS: - --all Generate configuration for all environments - --dry-run Show what would be generated without writing files - --verbose, -v Show detailed output - --help, -h Show this help message - -EXAMPLES: - bun scripts/generate-unified-config.ts development - bun scripts/generate-unified-config.ts production --verbose - bun scripts/generate-unified-config.ts --all - bun scripts/generate-unified-config.ts --dry-run - -GENERATED FILES: - πŸ“„ .env.[environment] - Environment variables - 🚒 k8s-kustomize/overlays/[env]/config.env - Kubernetes configuration - 🐳 docker/.env.[environment] - Docker environment - ⚑ .env.vite.[environment] - Vite-specific variables - πŸ“ config/generated/[env].config.ts - TypeScript config export - -The generator reads from config/unified.config.ts and creates all necessary -configuration files for the specified environment(s). -`); -} - -// Run CLI if this file is executed directly -if (import.meta.main) { - main().catch(console.error); -} - -export { ConfigGenerator, type GeneratorOptions }; diff --git a/scripts/gitea-deploy.sh b/scripts/gitea-deploy.sh deleted file mode 100755 index 5780adb..0000000 --- a/scripts/gitea-deploy.sh +++ /dev/null @@ -1,382 +0,0 @@ -#!/bin/bash - -# πŸ§ͺ Deployment Validation Script -# Validates complete deployment with all environment variables and health checks - -set -e - -echo "πŸš€ Starting deploymif docker compose -f docker/docker-compose.yaml -p rxminder-validation ps | grep -q "Up"; then - print_success "Docker Compose setup completed successfully!" -else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p rxminder-validation logsalidation..." - - # Colors for output - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - BLUE='\033[0;34m' - NC='\033[0m' # No Color - - # Function to print colored output - print_status() { - echo -e "${BLUE}[INFO]${NC} $1" - } - - print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" - } - - print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" - } - - print_error() { - echo -e "${RED}[ERROR]${NC} $1" - } - - # Cleanup function - cleanup() { - print_status "Cleaning up test containers..." - docker stop rxminder-validation-test 2>/dev/null || true - docker rm rxminder-validation-test 2>/dev/null || true - docker compose -f docker/docker-compose.yaml -p rxminder-validation down 2>/dev/null || true - } - - # Set trap for cleanup - trap cleanup EXIT - - print_status "1. Validating environment files..." - - # Check if required environment files exist - if [[ ! -f .env ]]; then - print_error ".env file not found. Run 'cp .env.example .env' and configure it." - exit 1 - fi - - if [[ ! -f .env.example ]]; then - print_error ".env.example file not found." - exit 1 - fi - - print_success "Environment files exist" - - # Validate environment consistency - print_status "2. Checking environment variable consistency..." - ./validate-env.sh - - print_status "3. Setting up Docker Buildx..." - - # Ensure buildx is available - if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx." - exit 1 - fi - - # Create a new builder instance if it doesn't exist - if ! docker buildx ls | grep -q "rxminder-builder"; then - print_status "Creating new buildx builder instance..." - docker buildx create --name rxminder-builder --driver docker-container --bootstrap - fi - - # Use the builder - docker buildx use rxminder-builder - - print_status "4. Building multi-platform Docker image with buildx..." - - # Build the image with buildx for multiple platforms - docker buildx build --no-cache \ - --platform linux/amd64,linux/arm64 \ - --build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \ - --build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \ - --build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \ - --build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \ - --build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \ - --build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \ - --build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \ - --build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \ - --build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \ - --build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \ - --build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \ - --build-arg NODE_ENV="${NODE_ENV:-production}" \ - -t rxminder-validation \ - --load \ - . - - print_success "Docker image built successfully" - - print_status "5. Testing container startup and health..." - - # Run container in background - docker run --rm -d \ - -p 8083:80 \ - --name rxminder-validation-test \ - rxminder-validation - - # Wait for container to start - sleep 5 - - # Check if container is running - if ! docker ps | grep -q rxminder-validation-test; then - print_error "Container failed to start" - docker logs rxminder-validation-test - exit 1 - fi - - print_success "Container started successfully" - - # Test health endpoint - print_status "5. Testing health endpoint..." - for i in {1..10}; do - if curl -s -f http://localhost:8083/health > /dev/null; then - print_success "Health endpoint responding" - break - elif [[ $i -eq 10 ]]; then - print_error "Health endpoint not responding after 10 attempts" - exit 1 - else - print_warning "Health endpoint not ready, retrying... ($i/10)" - sleep 2 - fi - done - - # Test main application - print_status "6. Testing main application..." - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083) - if [[ $HTTP_CODE -eq 200 ]]; then - print_success "Main application responding (HTTP $HTTP_CODE)" - else - print_error "Main application not responding properly (HTTP $HTTP_CODE)" - exit 1 - fi - - # Test docker-compose build - print_status "7. Testing Docker Compose build..." - docker compose -f docker/docker-compose.yaml build frontend --no-cache - - print_success "Docker Compose build successful" - - # Test docker-compose with validation project name - print_status "8. Testing Docker Compose deployment..." - docker compose -f docker/docker-compose.yaml -p rxminder-validation up -d --build - - # Wait for services to start - sleep 10 - - # Check service health - if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then - print_success "Docker Compose services started successfully" - else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p meds-validation logs - exit 1 - fi - - # Test health of compose deployment - if curl -s -f http://localhost:8080/health > /dev/null; then - print_success "Docker Compose health endpoint responding" - else - print_warning "Docker Compose health endpoint not responding (may need CouchDB)" - fi - - print_status "9. Checking image size..." - IMAGE_SIZE=$(docker image inspect rxminder-validation --format='{{.Size}}' | numfmt --to=iec) - print_success "Image size: $IMAGE_SIZE" - - print_status "10. Validating security configuration..." - - # Check if image runs as non-root - USER_INFO=$(docker run --rm rxminder-validation whoami) - if [[ "$USER_INFO" != "root" ]]; then - print_success "Container runs as non-root user: $USER_INFO" - else - print_warning "Container runs as root user (security consideration)" - fi - - # Check nginx configuration - if docker run --rm rxminder-validation nginx -t 2>/dev/null; then - print_success "Nginx configuration is valid" - else - print_error "Nginx configuration has issues" - exit 1 - fi - - print_status "11. Final validation complete!" - - echo - echo "πŸŽ‰ Deployment validation successful!" - echo - echo "Summary:" - echo "βœ… Environment files validated" - echo "βœ… Docker image builds successfully" - echo "βœ… Container starts and runs healthy" - echo "βœ… Health endpoints respond correctly" - echo "βœ… Docker Compose deployment works" - echo "βœ… Security configuration validated" - echo "βœ… Image size optimized ($IMAGE_SIZE)" - echo - echo "Your deployment is ready for production! πŸš€" - echo - echo "Next steps:" - echo "1. Configure production environment variables in .env" - echo "2. Run './deploy.sh production' for production deployment" - echo "3. Set up monitoring and backups" - echo "4. Configure SSL/TLS certificates" - echo - - # Load Gitea-specific environment variables - REGISTRY=${GITEA_SERVER_URL#https://} - IMAGE_NAME=${GITEA_REPOSITORY} - IMAGE_TAG=${GITEA_SHA:0:8} - - print_status "Registry: $REGISTRY" - print_status "Image: $IMAGE_NAME:$IMAGE_TAG" -fi - -# Check if .env file exists -if [ ! -f ".env" ]; then - print_warning ".env file not found, using defaults" - - # Create minimal .env for Gitea deployment - cat > .env << EOF -COUCHDB_USER=admin -COUCHDB_PASSWORD=change-this-secure-password -VITE_COUCHDB_URL=http://couchdb:5984 -VITE_COUCHDB_USER=admin -VITE_COUCHDB_PASSWORD=change-this-secure-password -APP_BASE_URL=http://localhost:8080 -NODE_ENV=production -EOF - - print_warning "Created default .env file - please update with your credentials" -fi - -# Load environment variables -print_status "Loading environment variables from .env file..." -export $(cat .env | grep -v '^#' | xargs) - -# Validate required environment variables -REQUIRED_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "VITE_COUCHDB_URL") -for var in "${REQUIRED_VARS[@]}"; do - if [ -z "${!var}" ]; then - print_error "Required environment variable $var is not set" - exit 1 - fi -done - -print_success "Environment variables validated" - -# Function to deploy via Docker Compose -deploy_compose() { - print_status "Deploying with Docker Compose..." - - # Export image variables for compose - export IMAGE_TAG - export REGISTRY - export IMAGE_NAME - - # Use the built image from registry if available - if [ "$GITEA_ACTIONS" = "true" ]; then - # Override the image in docker-compose - export FRONTEND_IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" - print_status "Using Gitea Actions built image: $FRONTEND_IMAGE" - fi - - # Pull the latest images - print_status "Pulling latest images..." - docker-compose -f docker/docker-compose.yaml pull || print_warning "Failed to pull some images" - - # Start services - print_status "Starting services..." - docker-compose -f docker/docker-compose.yaml up -d - - # Wait for services - print_status "Waiting for services to be ready..." - sleep 10 - - # Health check - print_status "Checking service health..." - if curl -f http://localhost:8080/health > /dev/null 2>&1; then - print_success "Frontend service is healthy" - else - print_warning "Frontend health check failed, checking logs..." - docker-compose -f docker/docker-compose.yaml logs frontend - fi - - if curl -f http://localhost:5984/_up > /dev/null 2>&1; then - print_success "CouchDB service is healthy" - else - print_warning "CouchDB health check failed" - fi -} - -# Function to deploy via Kubernetes -deploy_k8s() { - print_status "Deploying to Kubernetes..." - - if ! command -v kubectl &> /dev/null; then - print_error "kubectl is not installed" - exit 1 - fi - - # Update image in k8s manifests - if [ "$GITEA_ACTIONS" = "true" ]; then - print_status "Updating Kubernetes manifests with new image..." - sed -i "s|image:.*rxminder.*|image: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG|g" k8s/frontend-deployment.yaml - fi - - # Apply manifests - print_status "Applying Kubernetes manifests..." - kubectl apply -f k8s/ - - # Wait for rollout - print_status "Waiting for deployment rollout..." - kubectl rollout status deployment/frontend-deployment - - print_success "Kubernetes deployment completed" -} - -# Main deployment logic -case "$ENVIRONMENT" in - "production"|"prod") - print_status "Deploying to production environment" - deploy_compose - ;; - "kubernetes"|"k8s") - print_status "Deploying to Kubernetes environment" - deploy_k8s - ;; - "staging") - print_status "Deploying to staging environment" - # Use staging-specific configurations - export APP_BASE_URL="http://staging.localhost:8080" - deploy_compose - ;; - *) - print_error "Unknown environment: $ENVIRONMENT" - echo "Available environments: production, kubernetes, staging" - exit 1 - ;; -esac - -print_success "Deployment to $ENVIRONMENT completed successfully! πŸŽ‰" - -# Post-deployment tasks -print_status "Running post-deployment tasks..." - -# Cleanup old images (optional) -if [ "$CLEANUP_OLD_IMAGES" = "true" ]; then - print_status "Cleaning up old Docker images..." - docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed" -fi - -# Send notification (if configured) -if [ -n "$DEPLOYMENT_WEBHOOK_URL" ]; then - print_status "Sending deployment notification..." - curl -X POST "$DEPLOYMENT_WEBHOOK_URL" \ - -H "Content-Type: application/json" \ - -d "{\"text\":\"βœ… RxMinder deployed to $ENVIRONMENT\", \"environment\":\"$ENVIRONMENT\", \"image\":\"$REGISTRY/$IMAGE_NAME:$IMAGE_TAG\"}" \ - || print_warning "Failed to send notification" -fi - -print_success "All tasks completed! πŸš€" diff --git a/scripts/gitea-helper.sh b/scripts/gitea-helper.sh deleted file mode 100755 index d2a8122..0000000 --- a/scripts/gitea-helper.sh +++ /dev/null @@ -1,518 +0,0 @@ -#!/bin/bash - -# gitea-deploy.sh - Gitea-specific deployment script -# Usage: ./gitea-deploy.sh [environment] [image-tag] - -set -e # Exit on any error - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - -# Load environment variables from .env file if it exists -if [ -f "$PROJECT_DIR/.env" ]; then - export $(cat "$PROJECT_DIR/.env" | grep -v '^#' | grep -E '^[A-Z_]+=.*' | xargs) -fi - -ENVIRONMENT=${1:-production} -IMAGE_TAG=${2:-latest} -REGISTRY=${GITEA_REGISTRY:-${CONTAINER_REGISTRY:-"ghcr.io"}} -IMAGE_NAME=${GITEA_REPOSITORY:-${CONTAINER_REPOSITORY:-"rxminder"}} - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -print_status() { - echo -e "${BLUE}ℹ️ $1${NC}" -} - -print_success() { - echo -e "${GREEN}βœ… $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠️ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -echo "πŸš€ Deploying RxMinder from Gitea to $ENVIRONMENT environment..." - -# Check if running in Gitea Actions -if [ "$GITEA_ACTIONS" = "true" ]; then - print_status "Running in Gitea Actions environment" - - # Load Gitea-specific environment variables - REGISTRY=${GITEA_SERVER_URL#https://} - IMAGE_NAME=${GITEA_REPOSITORY} - IMAGE_TAG=${GITEA_SHA:0:8} - - print_status "Registry: $REGISTRY" - print_status "Image: $IMAGE_NAME:$IMAGE_TAG" -fi - -# Check if .env file exists -if [ ! -f ".env" ]; then - print_warning ".env file not found, using defaults" - - # Create minimal .env for Gitea deployment - cat > .env << EOF -COUCHDB_USER=admin -COUCHDB_PASSWORD=change-this-secure-password -VITE_COUCHDB_URL=http://couchdb:5984 -VITE_COUCHDB_USER=admin -VITE_COUCHDB_PASSWORD=change-this-secure-password -APP_BASE_URL=http://localhost:8080 -NODE_ENV=production -EOF - - print_warning "Created default .env file - please update with your credentials" -fi - -# Load environment variables -print_status "Loading environment variables from .env file..." -export $(cat .env | grep -v '^#' | xargs) - -# Validate required environment variables -REQUIRED_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "VITE_COUCHDB_URL") -for var in "${REQUIRED_VARS[@]}"; do - if [ -z "${!var}" ]; then - print_error "Required environment variable $var is not set" - exit 1 - fi -done - -print_success "Environment variables validated" - -# Function to deploy via Docker Compose -deploy_compose() { - print_status "Deploying with Docker Compose..." - - # Export image variables for compose - export IMAGE_TAG - export REGISTRY - export IMAGE_NAME - - # Use the built image from registry if available - if [ "$GITEA_ACTIONS" = "true" ]; then - # Override the image in docker-compose - export FRONTEND_IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" - print_status "Using Gitea Actions built image: $FRONTEND_IMAGE" - fi - - # Pull the latest images - print_status "Pulling latest images..." - docker-compose -f docker/docker-compose.yaml pull || print_warning "Failed to pull some images" - - # Start services - print_status "Starting services..." - docker-compose -f docker/docker-compose.yaml up -d - - # Wait for services - print_status "Waiting for services to be ready..." - sleep 10 - - # Health check - print_status "Checking service health..." - if curl -f http://localhost:8080/health > /dev/null 2>&1; then - print_success "Frontend service is healthy" - else - print_warning "Frontend health check failed, checking logs..." - docker-compose -f docker/docker-compose.yaml logs frontend - fi - - if curl -f http://localhost:5984/_up > /dev/null 2>&1; then - print_success "CouchDB service is healthy" - else - print_warning "CouchDB health check failed" - fi -} - -# Function to deploy via Kubernetes -deploy_k8s() { - print_status "Deploying to Kubernetes..." - - if ! command -v kubectl &> /dev/null; then - print_error "kubectl is not installed" - exit 1 - fi - - # Update image in k8s manifests - if [ "$GITEA_ACTIONS" = "true" ]; then - print_status "Updating Kubernetes manifests with new image..." - sed -i "s|image:.*rxminder.*|image: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG|g" k8s/frontend-deployment.yaml - fi - - # Apply manifests - print_status "Applying Kubernetes manifests..." - kubectl apply -f k8s/ - - # Wait for rollout - print_status "Waiting for deployment rollout..." - kubectl rollout status deployment/frontend-deployment - - print_success "Kubernetes deployment completed" -} - -# Main deployment logic -case "$ENVIRONMENT" in - "production"|"prod") - print_status "Deploying to production environment" - deploy_compose - ;; - "kubernetes"|"k8s") - print_status "Deploying to Kubernetes environment" - deploy_k8s - ;; - "staging") - print_status "Deploying to staging environment" - # Use staging-specific configurations - export APP_BASE_URL="http://staging.localhost:8080" - deploy_compose - ;; - *) - print_error "Unknown environment: $ENVIRONMENT" - echo "Available environments: production, kubernetes, staging" - exit 1 - ;; -esac - -print_success "Deployment to $ENVIRONMENT completed successfully! πŸŽ‰" - -# Post-deployment tasks -print_status "Running post-deployment tasks..." - -# Cleanup old images (optional) -if [ "$CLEANUP_OLD_IMAGES" = "true" ]; then - print_status "Cleaning up old Docker images..." - docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed" -fi - -# Send notification (if configured) -if [ -n "$DEPLOYMENT_WEBHOOK_URL" ]; then - print_status "Sending deployment notification..." - curl -X POST "$DEPLOYMENT_WEBHOOK_URL" \ - -H "Content-Type: application/json" \ - -d "{\"text\":\"βœ… RxMinder deployed to $ENVIRONMENT\", \"environment\":\"$ENVIRONMENT\", \"image\":\"$REGISTRY/$IMAGE_NAME:$IMAGE_TAG\"}" \ - || print_warning "Failed to send notification" -fi - -print_success "All tasks completed! πŸš€" - print_error "Docker is not installed" - exit 1 - fi - - # Check Docker Buildx - if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available" - exit 1 - fi - - # Check if in Gitea Actions environment - if [ "$GITEA_ACTIONS" = "true" ]; then - print_status "Running in Gitea Actions environment" - GITEA_REGISTRY=${GITEA_SERVER_URL#https://} - GITEA_REPOSITORY=${GITEA_REPOSITORY} - fi - - print_success "All requirements met" -} - -setup_buildx() { - print_status "Setting up Docker Buildx for Gitea..." - - # Create builder if it doesn't exist - if ! docker buildx ls | grep -q "gitea-builder"; then - print_status "Creating Gitea buildx builder..." - docker buildx create \ - --name gitea-builder \ - --driver docker-container \ - --bootstrap \ - --use - print_success "Gitea builder created" - else - docker buildx use gitea-builder - print_success "Using existing Gitea builder" - fi -} - -login_registry() { - print_status "Logging into Gitea registry..." - - if [ -z "$GITEA_TOKEN" ]; then - print_error "GITEA_TOKEN environment variable is required" - print_status "Set it with: export GITEA_TOKEN=your_token" - exit 1 - fi - - # Login to Gitea registry - echo "$GITEA_TOKEN" | docker login "$GITEA_REGISTRY" -u "$GITEA_ACTOR" --password-stdin - print_success "Logged into Gitea registry" -} - -build_local() { - print_status "Building for local development..." - - cd "$PROJECT_DIR" - - # Load environment variables - if [ -f ".env" ]; then - export $(cat .env | grep -v '^#' | xargs) - fi - - # Build with Gitea bake file - docker buildx bake \ - -f .gitea/gitea-bake.hcl \ - --set="*.platform=linux/amd64" \ - --load \ - dev - - print_success "Local build completed" -} - -build_multiplatform() { - local tag=${1:-$DEFAULT_TAG} - print_status "Building multi-platform image with tag: $tag..." - - cd "$PROJECT_DIR" - - # Load environment variables - if [ -f ".env" ]; then - export $(cat .env | grep -v '^#' | xargs) - fi - - # Export variables for bake - export TAG="$tag" - export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "dev")} - - # Build with Gitea bake file - docker buildx bake \ - -f .gitea/gitea-bake.hcl \ - app-ci - - print_success "Multi-platform build completed" -} - -build_staging() { - print_status "Building staging image..." - - cd "$PROJECT_DIR" - - # Load environment variables - if [ -f ".env.staging" ]; then - export $(cat .env.staging | grep -v '^#' | xargs) - elif [ -f ".env" ]; then - export $(cat .env | grep -v '^#' | xargs) - fi - - # Export variables for bake - export TAG="staging-$(date +%Y%m%d-%H%M%S)" - export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "staging")} - - # Build staging target - docker buildx bake \ - -f .gitea/gitea-bake.hcl \ - staging - - print_success "Staging build completed" -} - -build_production() { - local tag=${1:-$DEFAULT_TAG} - print_status "Building production image with tag: $tag..." - - cd "$PROJECT_DIR" - - # Load production environment variables - if [ -f ".env.production" ]; then - export $(cat .env.production | grep -v '^#' | xargs) - elif [ -f ".env" ]; then - export $(cat .env | grep -v '^#' | xargs) - fi - - # Export variables for bake - export TAG="$tag" - export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "prod")} - - # Build production target with full attestations - docker buildx bake \ - -f .gitea/gitea-bake.hcl \ - prod - - print_success "Production build completed" -} - -test_local() { - print_status "Running tests locally..." - - cd "$PROJECT_DIR" - - # Install dependencies if needed - if [ ! -d "node_modules" ]; then - print_status "Installing dependencies..." - bun install --frozen-lockfile - fi - - # Run linting - print_status "Running linter..." - bun run lint - - # Run type checking - print_status "Running type checker..." - bun run type-check - - # Run tests - print_status "Running tests..." - bun run test - - print_success "All tests passed" -} - -deploy() { - local environment=${1:-production} - local tag=${2:-latest} - - print_status "Deploying to $environment with tag $tag..." - - # Use the gitea-deploy script - "$SCRIPT_DIR/gitea-deploy.sh" "$environment" "$tag" -} - -cleanup() { - print_status "Cleaning up Gitea builder and images..." - - # Remove builder - if docker buildx ls | grep -q "gitea-builder"; then - docker buildx rm gitea-builder - print_success "Gitea builder removed" - fi - - # Clean up old images (keep last 3 tags) - print_status "Cleaning up old images..." - docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed" - - print_success "Cleanup completed" -} - -show_status() { - print_status "Gitea CI/CD Status" - echo - - # Check environment - if [ "$GITEA_ACTIONS" = "true" ]; then - echo "πŸƒ Running in Gitea Actions" - echo "πŸ“¦ Registry: $GITEA_REGISTRY" - echo "πŸ“‹ Repository: $GITEA_REPOSITORY" - echo "🏷️ SHA: ${GITEA_SHA:-unknown}" - echo "🌿 Branch: ${GITEA_REF_NAME:-unknown}" - else - echo "πŸ’» Running locally" - echo "πŸ“¦ Registry: $GITEA_REGISTRY" - echo "πŸ“‹ Repository: $GITEA_REPOSITORY" - fi - - echo - - # Check Docker and buildx - echo "🐳 Docker version: $(docker --version)" - echo "πŸ”§ Buildx version: $(docker buildx version)" - - # Check builders - echo - echo "πŸ—οΈ Available builders:" - docker buildx ls -} - -show_help() { - cat << EOF -Gitea CI/CD Helper for RxMinder - -Usage: $0 [command] [options] - -Commands: - setup - Setup buildx builder for Gitea - login - Login to Gitea registry - build-local - Build for local development - build-multi [tag] - Build multi-platform image - build-staging - Build staging image - build-prod [tag] - Build production image - test - Run tests locally - deploy [env] [tag] - Deploy to environment - cleanup - Cleanup builders and images - status - Show CI/CD status - help - Show this help - -Examples: - $0 setup - $0 build-local - $0 build-multi v1.2.3 - $0 build-staging - $0 build-prod v1.2.3 - $0 test - $0 deploy production v1.2.3 - $0 deploy staging - $0 status - -Environment Variables: - GITEA_REGISTRY - Gitea registry URL (default: gitea.example.com) - GITEA_REPOSITORY - Repository name (default: user/rxminder) - GITEA_TOKEN - Gitea access token (required for registry) - GITEA_ACTOR - Gitea username (for registry login) - -EOF -} - -# Main command handling -case "${1:-help}" in - "setup") - check_requirements - setup_buildx - ;; - "login") - check_requirements - login_registry - ;; - "build-local") - check_requirements - setup_buildx - build_local - ;; - "build-multi") - check_requirements - setup_buildx - login_registry - build_multiplatform "$2" - ;; - "build-staging") - check_requirements - setup_buildx - login_registry - build_staging - ;; - "build-prod") - check_requirements - setup_buildx - login_registry - build_production "$2" - ;; - "test") - test_local - ;; - "deploy") - deploy "$2" "$3" - ;; - "cleanup") - cleanup - ;; - "status") - show_status - ;; - "help"|*) - show_help - ;; -esac diff --git a/scripts/k8s-deploy-template.sh b/scripts/k8s-deploy-template.sh deleted file mode 100755 index c48bfcf..0000000 --- a/scripts/k8s-deploy-template.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/env bash - -# πŸš€ RxMinder Template-based Kubernetes Deployment Script -# This script processes template files and applies them to Kubernetes - -set -euo pipefail - -# Script configuration -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -K8S_DIR="$(dirname "$SCRIPT_DIR")/k8s" -ENV_FILE="$(dirname "$SCRIPT_DIR")/.env" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -print_status() { - echo -e "${BLUE}$1${NC}" -} - -print_success() { - echo -e "${GREEN}βœ… $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠️ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -# Function to load environment variables -load_env() { - if [[ -f "$ENV_FILE" ]]; then - print_status "Loading environment variables from .env..." - # Export variables from .env file - set -a - source "$ENV_FILE" - set +a - print_success "Environment variables loaded" - else - print_warning ".env file not found at $ENV_FILE" - print_warning "Using default values. Copy .env.example to .env and customize." - fi -} - -# Function to substitute environment variables in templates -substitute_template() { - local template_file="$1" - local output_file="$2" - - print_status "Processing template: $template_file" - - # Use envsubst to substitute environment variables - if command -v envsubst >/dev/null 2>&1; then - envsubst < "$template_file" > "$output_file" - else - print_error "envsubst not found. Please install gettext package." - exit 1 - fi - - print_success "Generated: $output_file" -} - -# Function to apply Kubernetes resources -apply_k8s_resource() { - local resource_file="$1" - - if [[ -f "$resource_file" ]]; then - print_status "Applying Kubernetes resource: $resource_file" - if kubectl apply -f "$resource_file"; then - print_success "Applied: $resource_file" - else - print_error "Failed to apply: $resource_file" - return 1 - fi - else - print_warning "Resource file not found: $resource_file" - fi -} - -# Function to validate required environment variables -validate_env() { - local required_vars=( - "APP_NAME" - "DOCKER_IMAGE" - "COUCHDB_USER" - "COUCHDB_PASSWORD" - "INGRESS_HOST" - "STORAGE_CLASS" - "STORAGE_SIZE" - ) - - local missing_vars=() - - for var in "${required_vars[@]}"; do - if [[ -z "${!var:-}" ]]; then - missing_vars+=("$var") - fi - done - - if [[ ${#missing_vars[@]} -gt 0 ]]; then - print_error "Missing required environment variables:" - for var in "${missing_vars[@]}"; do - echo -e " ${RED}- $var${NC}" - done - print_warning "Please update your .env file with these variables." - exit 1 - fi - - print_success "All required environment variables are set" -} - -# Function to process all templates -process_templates() { - local temp_dir="/tmp/rxminder-k8s-$$" - mkdir -p "$temp_dir" - - print_status "Processing Kubernetes templates..." - - # Find all template files - local template_files=( - "$K8S_DIR/couchdb-secret.yaml.template" - "$K8S_DIR/ingress.yaml.template" - ) - - # Add any additional template files - for template_file in "$K8S_DIR"/*.template; do - if [[ -f "$template_file" ]]; then - template_files+=("$template_file") - fi - done - - # Process each template - for template_file in "${template_files[@]}"; do - if [[ -f "$template_file" ]]; then - local base_name - base_name="$(basename "$template_file" .template)" - local output_file="$temp_dir/$base_name" - substitute_template "$template_file" "$output_file" - fi - done - - echo "$temp_dir" -} - -# Function to deploy resources in correct order -deploy_resources() { - local resource_dir="$1" - - print_status "Deploying Kubernetes resources..." - - # Deploy in specific order for dependencies - local deployment_order=( - "couchdb-secret.yaml" - "couchdb-pvc.yaml" - "couchdb-service.yaml" - "couchdb-statefulset.yaml" - "configmap.yaml" - "frontend-deployment.yaml" - "frontend-service.yaml" - "ingress.yaml" - "$K8S_DIR/network-policy.yaml" - "$K8S_DIR/hpa.yaml" - ) - - for resource in "${deployment_order[@]}"; do - if [[ "$resource" == *.yaml ]]; then - # Check if it's a template-generated file - if [[ -f "$resource_dir/$(basename "$resource")" ]]; then - apply_k8s_resource "$resource_dir/$(basename "$resource")" - else - # Apply directly from k8s directory - apply_k8s_resource "$resource" - fi - fi - done -} - -# Function to run database seeding job -run_db_seed() { - print_status "Running database seed job..." - - # Apply the db-seed-job (which uses environment variables from secret) - if kubectl apply -f "$K8S_DIR/db-seed-job.yaml"; then - print_success "Database seed job submitted" - - # Wait for job completion - print_status "Waiting for database seed job to complete..." - if kubectl wait --for=condition=complete --timeout=300s job/db-seed-job; then - print_success "Database seeding completed successfully" - else - print_warning "Database seed job may have failed. Check logs:" - echo "kubectl logs job/db-seed-job" - fi - else - print_error "Failed to apply database seed job" - return 1 - fi -} - -# Function to display deployment status -show_status() { - print_status "Deployment Status:" - echo - - print_status "Pods:" - kubectl get pods -l app="${APP_NAME:-rxminder}" - echo - - print_status "Services:" - kubectl get services -l app="${APP_NAME:-rxminder}" - echo - - print_status "Ingress:" - kubectl get ingress - echo - - if [[ -n "${INGRESS_HOST:-}" ]]; then - print_success "Application should be available at: http://${INGRESS_HOST}" - fi -} - -# Function to cleanup temporary files -cleanup() { - if [[ -n "${temp_dir:-}" && -d "$temp_dir" ]]; then - rm -rf "$temp_dir" - fi -} - -# Main deployment function -main() { - local command="${1:-deploy}" - - case "$command" in - "deploy"|"apply") - print_status "πŸš€ Starting RxMinder Kubernetes deployment..." - echo - - # Set default values for required variables - export APP_NAME="${APP_NAME:-rxminder}" - export DOCKER_IMAGE="${DOCKER_IMAGE:-gitea-http.taildb3494.ts.net/will/meds:latest}" - export COUCHDB_USER="${COUCHDB_USER:-admin}" - export COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" - export INGRESS_HOST="${INGRESS_HOST:-rxminder.local}" - export STORAGE_CLASS="${STORAGE_CLASS:-longhorn}" - export STORAGE_SIZE="${STORAGE_SIZE:-5Gi}" - - load_env - validate_env - - # Process templates - temp_dir=$(process_templates) - trap cleanup EXIT - - # Deploy resources - deploy_resources "$temp_dir" - - # Run database seeding - run_db_seed - - # Show status - echo - show_status - - print_success "πŸŽ‰ RxMinder deployment completed!" - ;; - - "status") - load_env - show_status - ;; - - "delete"|"cleanup") - print_status "πŸ—‘οΈ Cleaning up RxMinder deployment..." - kubectl delete all,pvc,secret,configmap,ingress -l app="${APP_NAME:-rxminder}" || true - kubectl delete job db-seed-job || true - print_success "Cleanup completed" - ;; - - "help"|"-h"|"--help") - echo "RxMinder Kubernetes Deployment Script" - echo - echo "Usage: $0 [command]" - echo - echo "Commands:" - echo " deploy Deploy RxMinder to Kubernetes (default)" - echo " status Show deployment status" - echo " delete Delete all RxMinder resources" - echo " help Show this help message" - echo - echo "Environment variables (set in .env):" - echo " APP_NAME Application name (default: rxminder)" - echo " DOCKER_IMAGE Container image to deploy" - echo " COUCHDB_USER Database username (default: admin)" - echo " COUCHDB_PASSWORD Database password (required)" - echo " INGRESS_HOST Ingress hostname (required)" - echo " STORAGE_CLASS Storage class for PVCs (default: longhorn)" - echo " STORAGE_SIZE Storage size for database (default: 5Gi)" - ;; - - *) - print_error "Unknown command: $command" - echo "Use '$0 help' for usage information" - exit 1 - ;; - esac -} - -# Check if kubectl is available -if ! command -v kubectl >/dev/null 2>&1; then - print_error "kubectl not found. Please install kubectl and configure it to connect to your cluster." - exit 1 -fi - -# Check if envsubst is available -if ! command -v envsubst >/dev/null 2>&1; then - print_error "envsubst not found. Please install the gettext package:" - echo " Ubuntu/Debian: sudo apt-get install gettext" - echo " macOS: brew install gettext" - echo " RHEL/CentOS: sudo yum install gettext" - exit 1 -fi - -# Run main function -main "$@" diff --git a/scripts/pre-commit-checks.sh b/scripts/pre-commit-checks.sh deleted file mode 100755 index 10c5e6c..0000000 --- a/scripts/pre-commit-checks.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/bash - -# Enhanced pre-commit checks with parallel execution for speed -# This script runs multiple checks efficiently on staged files only - -set -e - -echo "πŸš€ Running pre-commit checks..." - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[PRE-COMMIT]${NC} $1" -} - -print_success() { - echo -e "${GREEN}βœ…${NC} $1" -} - -print_error() { - echo -e "${RED}❌${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}⚠️${NC} $1" -} - -# Check if there are any staged files -STAGED_FILES=$(git diff --cached --name-only) -if [ -z "$STAGED_FILES" ]; then - print_warning "No staged files found" - exit 0 -fi - -print_status "Found $(echo "$STAGED_FILES" | wc -l) staged files" - -# Create temporary directory for parallel job management -TEMP_DIR=$(mktemp -d) -trap "rm -rf $TEMP_DIR" EXIT - -# Function to run a command and capture its output -run_check() { - local name="$1" - local command="$2" - local output_file="$TEMP_DIR/$name.out" - local error_file="$TEMP_DIR/$name.err" - - { - echo "Running: $command" - eval "$command" > "$output_file" 2> "$error_file" - echo $? > "$TEMP_DIR/$name.exit" - } & - - echo $! > "$TEMP_DIR/$name.pid" -} - -# Start parallel checks -print_status "Starting parallel checks..." - -# 1. Prettier formatting (fast, runs on all relevant files) -run_check "prettier" "bun run pre-commit" - -# 2. Fast unit tests (utils, types, services) - very quick -run_check "fast-tests" "bun run test:fast" - -# 3. ESLint on staged JS/TS files only -STAGED_JS_TS_FILES=$(echo "$STAGED_FILES" | grep -E '\.(js|jsx|ts|tsx)$' || true) -if [ -n "$STAGED_JS_TS_FILES" ]; then - # Convert newlines to spaces for proper argument passing - ESLINT_FILES=$(echo "$STAGED_JS_TS_FILES" | tr '\n' ' ') - run_check "eslint" "bunx eslint --fix --max-warnings 0 $ESLINT_FILES" -else - echo "0" > "$TEMP_DIR/eslint.exit" - echo "No JS/TS files to lint" > "$TEMP_DIR/eslint.out" -fi - -# 4. TypeScript type checking on staged files -STAGED_TS_FILES=$(echo "$STAGED_FILES" | grep -E '\.(ts|tsx)$' || true) -if [ -n "$STAGED_TS_FILES" ]; then - run_check "typecheck" "./scripts/type-check-staged.sh" -else - echo "0" > "$TEMP_DIR/typecheck.exit" - echo "No TypeScript files to check" > "$TEMP_DIR/typecheck.out" -fi - -# 5. Markdown linting on staged markdown files -STAGED_MD_FILES=$(echo "$STAGED_FILES" | grep -E '\.md$' || true) -if [ -n "$STAGED_MD_FILES" ]; then - # Convert newlines to spaces for proper argument passing - MARKDOWN_FILES=$(echo "$STAGED_MD_FILES" | tr '\n' ' ') - run_check "markdown" "bunx markdownlint-cli2 --fix $MARKDOWN_FILES || echo 'Markdown linting failed but continuing...'" -else - echo "0" > "$TEMP_DIR/markdown.exit" - echo "No markdown files to lint" > "$TEMP_DIR/markdown.out" -fi - -# 6. Secret scanning on staged files (optional check) -if command -v secretlint > /dev/null; then - # Convert newlines to spaces for proper argument passing - SECRET_FILES=$(echo "$STAGED_FILES" | tr '\n' ' ') - run_check "secrets" "bunx secretlint $SECRET_FILES || echo 'Secret scanning failed but continuing...'" -else - echo "0" > "$TEMP_DIR/secrets.exit" - echo "secretlint not available, skipping secret scanning" > "$TEMP_DIR/secrets.out" -fi - -# Wait for all jobs to complete and collect results -print_status "Waiting for checks to complete..." - -FAILED_CHECKS=() -ALL_CHECKS=("prettier" "fast-tests" "eslint" "typecheck" "markdown" "secrets") - -for check in "${ALL_CHECKS[@]}"; do - if [ -f "$TEMP_DIR/$check.pid" ]; then - wait $(cat "$TEMP_DIR/$check.pid") 2>/dev/null || true - fi - - exit_code=$(cat "$TEMP_DIR/$check.exit" 2>/dev/null || echo "1") - - if [ "$exit_code" = "0" ]; then - print_success "$check passed" - else - # For some checks, failure is not critical - if [ "$check" = "secrets" ] || [ "$check" = "markdown" ] || [ "$check" = "fast-tests" ]; then - print_warning "$check had issues (non-critical)" - if [ -f "$TEMP_DIR/$check.out" ] && [ -s "$TEMP_DIR/$check.out" ]; then - echo -e "${YELLOW}$check output:${NC}" - cat "$TEMP_DIR/$check.out" - fi - else - print_error "$check failed" - FAILED_CHECKS+=("$check") - - # Show error output - if [ -f "$TEMP_DIR/$check.err" ] && [ -s "$TEMP_DIR/$check.err" ]; then - echo -e "${RED}$check errors:${NC}" - cat "$TEMP_DIR/$check.err" - fi - if [ -f "$TEMP_DIR/$check.out" ] && [ -s "$TEMP_DIR/$check.out" ]; then - echo -e "${YELLOW}$check output:${NC}" - cat "$TEMP_DIR/$check.out" - fi - fi - fi -done - -# Re-stage any files that were modified by formatters -git add $STAGED_FILES 2>/dev/null || true - -# Final result -if [ ${#FAILED_CHECKS[@]} -eq 0 ]; then - print_success "All critical pre-commit checks passed! πŸŽ‰" - echo "" - echo "Summary of checks:" - echo " βœ… Code formatting (Prettier)" - echo " βœ… Fast unit tests (Utils/Types/Services)" - echo " βœ… Code linting (ESLint)" - echo " βœ… Type checking (TypeScript)" - echo " ⚠️ Markdown linting (non-critical)" - echo " ⚠️ Secret scanning (optional)" - echo "" - exit 0 -else - print_error "The following critical checks failed: ${FAILED_CHECKS[*]}" - echo "" - echo "πŸ’‘ Tips to fix:" - echo " - Run 'bun run lint:fix' to auto-fix linting issues" - echo " - Run 'bun run format' to fix formatting issues" - echo " - Run 'bun run type-check' to see detailed type errors" - echo "" - echo "🚨 Critical failures prevent commit. Fix these issues first." - echo "" - exit 1 -fi diff --git a/scripts/process-html.sh b/scripts/process-html.sh deleted file mode 100755 index ef77c17..0000000 --- a/scripts/process-html.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Process HTML template with environment variables -# This script substitutes environment variables in index.html.template - -set -e - -# Default values -APP_NAME="${APP_NAME:-RxMinder}" - -# Input and output files -TEMPLATE_FILE="index.html.template" -OUTPUT_FILE="index.html" - -# Check if template exists -if [[ ! -f "$TEMPLATE_FILE" ]]; then - echo "Error: Template file $TEMPLATE_FILE not found" - exit 1 -fi - -# Process template with environment variable substitution -envsubst '$APP_NAME' < "$TEMPLATE_FILE" > "$OUTPUT_FILE" - -echo "βœ… Processed $TEMPLATE_FILE -> $OUTPUT_FILE with APP_NAME=$APP_NAME" diff --git a/scripts/seed-production.js b/scripts/seed-production.js deleted file mode 100644 index ad986ae..0000000 --- a/scripts/seed-production.js +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env node - -// Production database seeder script -// This script seeds the production CouchDB with default admin user - -import { readFileSync } from 'fs'; -import { resolve, dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const projectDir = resolve(__dirname, '..'); - -console.warn('🌱 Starting production database seeding...'); - -// Load environment variables from .env file if it exists -try { - const envFile = resolve(projectDir, '.env'); - const envContent = readFileSync(envFile, 'utf8'); - - console.warn('πŸ“„ Loading environment variables from .env file...'); - - envContent.split('\n').forEach(line => { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) { - const [key, ...valueParts] = trimmed.split('='); - const value = valueParts.join('='); - if (!process.env[key]) { - process.env[key] = value; - } - } - }); -} catch (_error) { - console.warn( - 'ℹ️ No .env file found, using environment variables or defaults' - ); -} - -// Use environment variables with fallbacks -const COUCHDB_URL = - process.env.VITE_COUCHDB_URL || - process.env.COUCHDB_URL || - 'http://localhost:5984'; -const COUCHDB_USER = - process.env.VITE_COUCHDB_USER || process.env.COUCHDB_USER || 'admin'; -const COUCHDB_PASSWORD = - process.env.VITE_COUCHDB_PASSWORD || - process.env.COUCHDB_PASSWORD || - 'change-this-secure-password'; - -// Set environment variables for the seeder to use -process.env.VITE_COUCHDB_URL = COUCHDB_URL; -process.env.VITE_COUCHDB_USER = COUCHDB_USER; -process.env.VITE_COUCHDB_PASSWORD = COUCHDB_PASSWORD; - -console.warn('πŸ”— CouchDB Configuration:'); -console.warn(` URL: ${COUCHDB_URL}`); -console.warn(` User: ${COUCHDB_USER}`); -console.warn(` Password: ${'*'.repeat(COUCHDB_PASSWORD.length)}`); - -// Validate required environment variables -if (!COUCHDB_URL || !COUCHDB_USER || !COUCHDB_PASSWORD) { - console.error('❌ Missing required environment variables:'); - console.error(' VITE_COUCHDB_URL or COUCHDB_URL'); - console.error(' VITE_COUCHDB_USER or COUCHDB_USER'); - console.error(' VITE_COUCHDB_PASSWORD or COUCHDB_PASSWORD'); - console.error(''); - console.error('πŸ’‘ Set these in your .env file or as environment variables'); - process.exit(1); -} - -async function seedDatabase() { - try { - // Import the seeder (this will use the production CouchDB due to env vars) - const { DatabaseSeeder } = await import('../services/database.seeder.ts'); - - // Wait a bit for databases to be initialized - console.warn('⏳ Waiting for databases to initialize...'); - await new Promise(resolve => setTimeout(resolve, 2000)); - - const seeder = new DatabaseSeeder(); - - console.warn('πŸ“Š Seeding admin user...'); - await seeder.seedDefaultAdmin(); - - console.warn('πŸŽ‰ Production database seeding completed successfully!'); - console.warn('πŸ” You can now login with:'); - console.warn(' Email: admin@localhost'); - console.warn(' Password: change-this-secure-password'); - - process.exit(0); - } catch (error) { - console.error('❌ Seeding failed:', error); - process.exit(1); - } -} - -seedDatabase(); diff --git a/scripts/setup-e2e.sh b/scripts/setup-e2e.sh deleted file mode 100755 index 9297fc2..0000000 --- a/scripts/setup-e2e.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -# 🎭 Playwright E2E Test Setup Script - -echo "🎭 Setting up Playwright for E2E testing..." - -# Check if we're in the right directory -if [[ ! -f "package.json" ]]; then - echo "❌ Error: Must be run from project root directory" - exit 1 -fi - -# Install Playwright if not already installed -echo "πŸ“¦ Installing Playwright..." -if command -v bun &> /dev/null; then - bun add -D @playwright/test -else - npm install -D @playwright/test -fi - -# Install browser binaries -echo "🌐 Installing browser binaries..." -bunx playwright install - -# Install system dependencies (Linux) -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - echo "🐧 Installing system dependencies for Linux..." - bunx playwright install-deps -fi - -# Create .gitignore entries for Playwright -echo "πŸ“ Updating .gitignore for Playwright artifacts..." -if ! grep -q "test-results" .gitignore; then - echo "" >> .gitignore - echo "# Playwright artifacts" >> .gitignore - echo "test-results/" >> .gitignore - echo "playwright-report/" >> .gitignore - echo "playwright/.cache/" >> .gitignore -fi - -# Verify installation -echo "βœ… Verifying Playwright installation..." -bunx playwright --version - -echo "" -echo "πŸŽ‰ Playwright setup complete!" -echo "" -echo "πŸ“‹ Quick start commands:" -echo " bun run test:e2e # Run all E2E tests" -echo " bun run test:e2e:ui # Run tests in UI mode" -echo " bun run test:e2e:debug # Debug tests" -echo " bun run test:e2e:report # View test report" -echo "" -echo "πŸ“š Documentation: tests/e2e/README.md" -echo "βš™οΈ Configuration: playwright.config.ts" diff --git a/scripts/setup-pre-commit.sh b/scripts/setup-pre-commit.sh deleted file mode 100755 index 0f5820c..0000000 --- a/scripts/setup-pre-commit.sh +++ /dev/null @@ -1,133 +0,0 @@ -# Run lint-staged for file-specific checks -bunx lint-staged - -# Run type checking (doesn't need file filtering) -bun run type-check - -# Check for large files (similar to pre-commit check-added-large-files) -git diff --cached --name-only | while IFS= read -r file; do - if [ -f "$file" ]; then - size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo 0) - if [ "$size" -gt 500000 ]; then # 500KB limit - echo "Error: Large file detected: $file ($(echo $size | awk '{print int($1/1024)}')KB)" - echo "Consider using Git LFS for large files." - exit 1 - fi - fi -done - -# Check for merge conflict markers -if git diff --cached | grep -E "^[<>=]{7}" >/dev/null; then - echo "Error: Merge conflict markers detected" - exit 1 -fi - -# Check for private keys (basic check) -if git diff --cached --name-only | xargs grep -l "BEGIN.*PRIVATE KEY" 2>/dev/null; then - echo "Error: Private key detected in staged files" - exit 1 -fi - -echo "βœ… Pre-commit checks passed!" -NC='\033[0m' # No Color - -echo -e "${GREEN}Setting up NodeJS-native pre-commit hooks and code formatters...${NC}" - -# Check if we're in a git repository -if [ ! -d ".git" ]; then - echo -e "${RED}Error: Not a git repository. Please run this script from the project root.${NC}" - exit 1 -fi - -# Install dependencies -echo -e "${YELLOW}Installing dependencies...${NC}" -if command -v bun &> /dev/null; then - bun install -elif command -v npm &> /dev/null; then - npm install -else - echo -e "${RED}Error: Neither bun nor npm found. Please install one of them first.${NC}" - exit 1 -fi - -# Initialize Husky (NodeJS-native git hooks) -echo -e "${YELLOW}Setting up Husky git hooks...${NC}" -if command -v bun &> /dev/null; then - bunx husky init -elif command -v npm &> /dev/null; then - npx husky init -fi - -# Make pre-commit hook executable -chmod +x .husky/pre-commit - -# Run initial formatting -echo -e "${YELLOW}Running initial code formatting...${NC}" -if command -v bun &> /dev/null; then - bun run format -elif command -v npm &> /dev/null; then - bun run format -fi - -# Run initial linting -echo -e "${YELLOW}Running initial linting...${NC}" -if command -v bun &> /dev/null; then - bun run lint:fix -elif command -v npm &> /dev/null; then - bun run lint:fix -fi - -# Run initial markdown linting -echo -e "${YELLOW}Running initial markdown linting...${NC}" -if command -v bun &> /dev/null; then - bun run lint:markdown:fix || echo "Markdown linting completed with warnings" -elif command -v npm &> /dev/null; then - bun run lint:markdown:fix || echo "Markdown linting completed with warnings" -fi - -# Fix EditorConfig issues -echo -e "${YELLOW}Fixing EditorConfig issues...${NC}" -if command -v bun &> /dev/null; then - bun run fix:editorconfig || echo "EditorConfig fixes completed" -elif command -v npm &> /dev/null; then - bun run fix:editorconfig || echo "EditorConfig fixes completed" -fi - -echo -e "${GREEN}βœ… NodeJS-native pre-commit hooks and code formatters have been set up successfully!${NC}" -echo "" -echo -e "${YELLOW}What was configured:${NC}" -echo " βœ“ Husky git hooks (.husky/pre-commit)" -echo " βœ“ Prettier code formatter (.prettierrc)" -echo " βœ“ ESLint configuration (eslint.config.cjs)" -echo " βœ“ EditorConfig (.editorconfig)" -echo " βœ“ Markdownlint configuration (.markdownlint.json)" -echo " βœ“ Secretlint configuration (.secretlintrc.json)" -echo " βœ“ Lint-staged for efficient pre-commit formatting" -echo " βœ“ Dockerfilelint for Docker file validation" -echo "" -echo -e "${YELLOW}Available commands:${NC}" -echo " β€’ bun run format - Format all files with Prettier" -echo " β€’ bun run lint - Run ESLint on TypeScript/JavaScript files" -echo " β€’ bun run lint:fix - Run ESLint with auto-fix" -echo " β€’ bun run lint:markdown - Check Markdown files" -echo " β€’ bun run lint:markdown:fix - Fix Markdown files" -echo " β€’ bun run lint:docker - Check Dockerfile" -echo " β€’ bun run check:secrets - Check for secrets in files" -echo " β€’ bun run check:editorconfig - Check EditorConfig compliance" -echo " β€’ bun run fix:editorconfig - Fix EditorConfig issues" -echo " β€’ bun run type-check - Run TypeScript type checking" -echo "" -echo -e "${YELLOW}What happens on commit:${NC}" -echo " 1. Lint-staged runs on changed files:" -echo " β€’ ESLint auto-fix + Prettier formatting for JS/TS files" -echo " β€’ Prettier formatting for JSON/YAML/MD/CSS files" -echo " β€’ Markdownlint auto-fix for Markdown files" -echo " β€’ Dockerfilelint for Dockerfile" -echo " β€’ EditorConfig fixes for all files" -echo " 2. TypeScript type checking on entire project" -echo " 3. Large file detection (>500KB)" -echo " 4. Merge conflict marker detection" -echo " 5. Basic private key detection" -echo "" -echo -e "${GREEN}All commits will now be automatically checked and formatted! πŸŽ‰${NC}" -echo -e "${GREEN}This setup is 100% NodeJS-native - no Python dependencies required! πŸš€${NC}" diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100755 index 9ea1d4c..0000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/bash - -# πŸ§ͺ Deployment Validation Script -# Validates complete deployment with all environment variables and health checks - -set -e - -echo "πŸš€ Starting deploymif docker compose -f docker/docker-compose.yaml -p rxminder-validation ps | grep -q "Up"; then - print_success "Docker Compose setup completed successfully!" -else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p rxminder-validation logsalidation..." - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Cleanup function -cleanup() { - print_status "Cleaning up test containers..." - docker stop rxminder-validation-test 2>/dev/null || true - docker rm rxminder-validation-test 2>/dev/null || true - docker compose -f docker/docker-compose.yaml -p rxminder-validation down 2>/dev/null || true -} - -# Set trap for cleanup -trap cleanup EXIT - -print_status "1. Validating environment files..." - -# Check if required environment files exist -if [[ ! -f .env ]]; then - print_error ".env file not found. Run 'cp .env.example .env' and configure it." - exit 1 -fi - -if [[ ! -f .env.example ]]; then - print_error ".env.example file not found." - exit 1 -fi - -print_success "Environment files exist" - -# Validate environment consistency -print_status "2. Checking environment variable consistency..." -./validate-env.sh - -print_status "3. Setting up Docker Buildx..." - -# Ensure buildx is available -if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx." - exit 1 -fi - -# Create a new builder instance if it doesn't exist -if ! docker buildx ls | grep -q "rxminder-builder"; then - print_status "Creating new buildx builder instance..." - docker buildx create --name rxminder-builder --driver docker-container --bootstrap -fi - -# Use the builder -docker buildx use rxminder-builder - -print_status "4. Building multi-platform Docker image with buildx..." - -# Build the image with buildx for multiple platforms -docker buildx build --no-cache \ ---platform linux/amd64,linux/arm64 \ ---build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \ ---build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \ ---build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \ ---build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \ ---build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \ ---build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \ ---build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \ ---build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \ ---build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \ ---build-arg NODE_ENV="${NODE_ENV:-production}" \ --t rxminder-validation \ ---load \ -. - -print_success "Docker image built successfully" - -print_status "5. Testing container startup and health..." - -# Run container in background -docker run --rm -d \ --p 8083:80 \ ---name rxminder-validation-test \ -rxminder-validation - -# Wait for container to start -sleep 5 - -# Check if container is running -if ! docker ps | grep -q rxminder-validation-test; then - print_error "Container failed to start" - docker logs rxminder-validation-test - exit 1 -fi - -print_success "Container started successfully" - -# Test health endpoint -print_status "5. Testing health endpoint..." -for i in {1..10}; do - if curl -s -f http://localhost:8083/health > /dev/null; then - print_success "Health endpoint responding" - break - elif [[ $i -eq 10 ]]; then - print_error "Health endpoint not responding after 10 attempts" - exit 1 - else - print_warning "Health endpoint not ready, retrying... ($i/10)" - sleep 2 - fi -done - -# Test main application -print_status "6. Testing main application..." -HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083) -if [[ $HTTP_CODE -eq 200 ]]; then - print_success "Main application responding (HTTP $HTTP_CODE)" -else - print_error "Main application not responding properly (HTTP $HTTP_CODE)" - exit 1 -fi - -# Test docker-compose build -print_status "7. Testing Docker Compose build..." -docker compose -f docker/docker-compose.yaml build frontend --no-cache - -print_success "Docker Compose build successful" - -# Test docker-compose with validation project name -print_status "8. Testing Docker Compose deployment..." -docker compose -f docker/docker-compose.yaml -p rxminder-validation up -d --build - -# Wait for services to start -sleep 10 - -# Check service health -if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then - print_success "Docker Compose services started successfully" -else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p meds-validation logs - exit 1 -fi - -# Test health of compose deployment -if curl -s -f http://localhost:8080/health > /dev/null; then - print_success "Docker Compose health endpoint responding" -else - print_warning "Docker Compose health endpoint not responding (may need CouchDB)" -fi - -print_status "9. Checking image size..." -IMAGE_SIZE=$(docker image inspect rxminder-validation --format='{{.Size}}' | numfmt --to=iec) -print_success "Image size: $IMAGE_SIZE" - -print_status "10. Validating security configuration..." - -# Check if image runs as non-root -USER_INFO=$(docker run --rm rxminder-validation whoami) -if [[ "$USER_INFO" != "root" ]]; then - print_success "Container runs as non-root user: $USER_INFO" -else - print_warning "Container runs as root user (security consideration)" -fi - -# Check nginx configuration -if docker run --rm rxminder-validation nginx -t 2>/dev/null; then - print_success "Nginx configuration is valid" -else - print_error "Nginx configuration has issues" - exit 1 -fi - -print_status "11. Final validation complete!" - -echo -echo "πŸŽ‰ Deployment validation successful!" -echo -echo "Summary:" -echo "βœ… Environment files validated" -echo "βœ… Docker image builds successfully" -echo "βœ… Container starts and runs healthy" -echo "βœ… Health endpoints respond correctly" -echo "βœ… Docker Compose deployment works" -echo "βœ… Security configuration validated" -echo "βœ… Image size optimized ($IMAGE_SIZE)" -echo -echo "Your deployment is ready for production! πŸš€" -echo -echo "Next steps:" -echo "1. Configure production environment variables in .env" -echo "2. Run './deploy.sh production' for production deployment" -echo "3. Set up monitoring and backups" -echo "4. Configure SSL/TLS certificates" -echo diff --git a/scripts/type-check-staged.sh b/scripts/type-check-staged.sh deleted file mode 100755 index b4dcf84..0000000 --- a/scripts/type-check-staged.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -# Fast TypeScript type checking for staged files only -# This script checks only the staged TypeScript files for type errors - -set -e - -# Get list of staged TypeScript files -STAGED_TS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx)$' || true) - -if [ -z "$STAGED_TS_FILES" ]; then - echo "πŸ“ No TypeScript files staged, skipping type check" - exit 0 -fi - -echo "πŸ” Type checking staged TypeScript files..." - -# Check if we have a tsconfig.json -if [ ! -f "tsconfig.json" ]; then - echo "⚠️ No tsconfig.json found, skipping type check" - exit 0 -fi - -# Try using the project's existing TypeScript setup -# First, try a quick check with noEmit -if bunx tsc --noEmit --skipLibCheck --incremental false > /dev/null 2>&1; then - echo "βœ… TypeScript type check passed" - exit 0 -else - echo "⚠️ Full type check failed, checking individual staged files..." - - # Fallback: check each staged file individually with minimal config - FAILED_FILES=() - - for file in $STAGED_TS_FILES; do - if [ -f "$file" ]; then - # Basic syntax check without full type checking - if ! bunx tsc --noEmit --skipLibCheck --allowJs --jsx preserve "$file" > /dev/null 2>&1; then - FAILED_FILES+=("$file") - fi - fi - done - - if [ ${#FAILED_FILES[@]} -eq 0 ]; then - echo "βœ… TypeScript syntax check passed for staged files" - exit 0 - else - echo "❌ TypeScript errors found in: ${FAILED_FILES[*]}" - echo "πŸ’‘ Run 'bun run type-check' for detailed errors" - exit 1 - fi -fi diff --git a/scripts/undeploy-k8s.sh b/scripts/undeploy-k8s.sh deleted file mode 100755 index 6f399d8..0000000 --- a/scripts/undeploy-k8s.sh +++ /dev/null @@ -1,298 +0,0 @@ -#!/bin/bash - -# Kubernetes deployment script with environment variable substitution -# This script processes template files and applies them to Kubernetes - -set -euo pipefail - -# Color codes for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -K8S_DIR="$PROJECT_ROOT/k8s" -TEMP_DIR="/tmp/meds-k8s-deploy" - -# Function to print colored output -print_info() { - echo -e "${BLUE}ℹ️ $1${NC}" -} - -print_success() { - echo -e "${GREEN}βœ… $1${NC}" -} - -print_warning() { - echo -e "${YELLOW}⚠️ $1${NC}" -} - -print_error() { - echo -e "${RED}❌ $1${NC}" -} - -# Function to load environment variables -load_env() { - local env_file="$1" - if [[ -f "$env_file" ]]; then - print_info "Loading environment from $env_file" - # Export variables from .env file - set -a - source "$env_file" - set +a - else - print_warning "Environment file $env_file not found" - fi -} - -# Function to substitute environment variables in template files -substitute_templates() { - print_info "Processing template files..." - - # Create temporary directory - mkdir -p "$TEMP_DIR" - - # Process each template file - for template_file in "$K8S_DIR"/*.template; do - if [[ -f "$template_file" ]]; then - local filename=$(basename "$template_file" .template) - local output_file="$TEMP_DIR/$filename" - - print_info "Processing template: $filename" - - # Substitute environment variables - envsubst < "$template_file" > "$output_file" - - print_success "Generated: $output_file" - fi - done -} - -# Function to validate required environment variables -validate_env() { - local required_vars=("INGRESS_HOST") - local missing_vars=() - - for var in "${required_vars[@]}"; do - if [[ -z "${!var:-}" ]]; then - missing_vars+=("$var") - fi - done - - if [[ ${#missing_vars[@]} -gt 0 ]]; then - print_error "Missing required environment variables:" - for var in "${missing_vars[@]}"; do - echo " - $var" - done - echo "" - echo "Please set these variables in your .env file or environment." - exit 1 - fi -} - -# Function to ensure namespace exists -ensure_namespace() { - if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then - print_info "Creating namespace: $NAMESPACE" - kubectl create namespace "$NAMESPACE" - print_success "Created namespace: $NAMESPACE" - else - print_info "Using existing namespace: $NAMESPACE" - fi -} - -# Function to apply Kubernetes manifests -apply_manifests() { - local manifest_dir="$1" - - print_info "Applying Kubernetes manifests from $manifest_dir" - - # Apply static files that don't have template counterparts - for manifest_file in "$K8S_DIR"/*.yaml; do - if [[ -f "$manifest_file" && ! "$manifest_file" =~ \.template$ ]]; then - local basename_file=$(basename "$manifest_file") - local template_file="$K8S_DIR/${basename_file}.template" - - # Only apply if there's no corresponding template file - if [[ ! -f "$template_file" ]]; then - print_info "Applying static file: $basename_file" - kubectl apply -f "$manifest_file" -n "$NAMESPACE" - fi - fi - done - - # Apply processed template files - if [[ -d "$TEMP_DIR" ]]; then - for manifest_file in "$TEMP_DIR"/*.yaml; do - if [[ -f "$manifest_file" ]]; then - print_info "Applying template: $(basename "$manifest_file")" - kubectl apply -f "$manifest_file" -n "$NAMESPACE" - fi - done - fi -} - -# Function to cleanup temporary files -cleanup() { - if [[ -d "$TEMP_DIR" ]]; then - print_info "Cleaning up temporary files..." - rm -rf "$TEMP_DIR" - fi -} - -# Function to show deployment status -show_status() { - print_info "Deployment Status:" - echo "" - - print_info "Pods:" - kubectl get pods -l app=rxminder -n "$NAMESPACE" - echo "" - - print_info "Services:" - kubectl get services -l app=rxminder -n "$NAMESPACE" - echo "" - - print_info "Ingress:" - kubectl get ingress -l app=rxminder -n "$NAMESPACE" - echo "" - - if [[ -n "${INGRESS_HOST:-}" ]]; then - print_success "Application should be available at: http://$INGRESS_HOST" - fi -} - -# Function to show usage -usage() { - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Options:" - echo " -e, --env FILE Environment file to load (default: .env)" - echo " -d, --dry-run Show what would be applied without applying" - echo " -s, --status Show deployment status only" - echo " -c, --cleanup Cleanup temporary files and exit" - echo " -h, --help Show this help message" - echo "" - echo "Examples:" - echo " $0 Deploy with default .env file" - echo " $0 -e .env.prod Deploy with production environment" - echo " $0 --dry-run Preview what would be deployed" - echo " $0 --status Check deployment status" -} - -# Main function -main() { - local env_file=".env" - local dry_run=false - local status_only=false - local cleanup_only=false - - # Parse command line arguments - while [[ $# -gt 0 ]]; do - case $1 in - -e|--env) - env_file="$2" - shift 2 - ;; - -d|--dry-run) - dry_run=true - shift - ;; - -s|--status) - status_only=true - shift - ;; - -c|--cleanup) - cleanup_only=true - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - print_error "Unknown option: $1" - usage - exit 1 - ;; - esac - done - - # Handle cleanup only - if [[ "$cleanup_only" == true ]]; then - cleanup - exit 0 - fi - - # Handle status only - if [[ "$status_only" == true ]]; then - show_status - exit 0 - fi - - # Check if kubectl is available - if ! command -v kubectl &> /dev/null; then - print_error "kubectl is not installed or not in PATH" - exit 1 - fi - - # Check if we can connect to Kubernetes cluster - if ! kubectl cluster-info &> /dev/null; then - print_error "Cannot connect to Kubernetes cluster" - print_info "Make sure your kubectl is configured correctly" - exit 1 - fi - - print_info "πŸš€ Deploying Medication Reminder App to Kubernetes" - echo "" - - # Load environment variables - load_env "$env_file" - - # Set default namespace if not provided in environment - NAMESPACE="${NAMESPACE:-rxminder}" - - # Validate required environment variables - validate_env - - # Ensure namespace exists - ensure_namespace - - # Process templates - substitute_templates - - if [[ "$dry_run" == true ]]; then - print_info "Dry run mode - showing generated manifests:" - echo "" - - for manifest_file in "$TEMP_DIR"/*.yaml; do - if [[ -f "$manifest_file" ]]; then - echo "=== $(basename "$manifest_file") ===" - cat "$manifest_file" - echo "" - fi - done - else - # Apply manifests - apply_manifests "$K8S_DIR" - - print_success "Deployment completed!" - echo "" - - # Show status - show_status - fi - - # Cleanup - cleanup -} - -# Trap to ensure cleanup happens -trap cleanup EXIT - -# Run main function -main "$@" diff --git a/scripts/validate-deployment.sh b/scripts/validate-deployment.sh deleted file mode 100755 index 3df879a..0000000 --- a/scripts/validate-deployment.sh +++ /dev/null @@ -1,224 +0,0 @@ -#!/bin/bash - -# πŸ§ͺ Deployment Validation Script -# Validates complete deployment with all environment variables and health checks - -set -e - -echo "πŸš€ Starting deployment validation..." - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Function to print colored output -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Cleanup function -cleanup() { - print_status "Cleaning up test containers..." - APP_NAME_LOWER=$(echo "${APP_NAME:-meds}" | tr '[:upper:]' '[:lower:]') - docker stop ${APP_NAME_LOWER}-validation-test 2>/dev/null || true - docker rm ${APP_NAME_LOWER}-validation-test 2>/dev/null || true - docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation down 2>/dev/null || true -} - -# Set trap for cleanup -trap cleanup EXIT - -print_status "1. Validating environment files..." - -# Check if required environment files exist -if [[ ! -f .env ]]; then - print_error ".env file not found. Run 'cp .env.example .env' and configure it." - exit 1 -fi - -if [[ ! -f .env.example ]]; then - print_error ".env.example file not found." - exit 1 -fi - -print_success "Environment files exist" - -# Validate environment consistency -print_status "2. Checking environment variable consistency..." -./validate-env.sh - -print_status "3. Setting up Docker Buildx..." - -# Ensure buildx is available -if ! docker buildx version >/dev/null 2>&1; then - print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx." - exit 1 -fi - -# Create a new builder instance if it doesn't exist -if ! docker buildx ls | grep -q "meds-builder"; then - print_status "Creating new buildx builder instance..." - docker buildx create --name meds-builder --driver docker-container --bootstrap -fi - -# Use the builder -docker buildx use meds-builder - -print_status "4. Building multi-platform Docker image with buildx..." - -# Build single-platform image for testing -APP_NAME_LOWER=$(echo "${APP_NAME:-meds}" | tr '[:upper:]' '[:lower:]') -docker buildx build --no-cache \ ---platform linux/amd64 \ ---build-arg APP_NAME="${APP_NAME:-RxMinder}" \ ---build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \ ---build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \ ---build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \ ---build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \ ---build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \ ---build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \ ---build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \ ---build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \ ---build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \ ---build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \ ---build-arg NODE_ENV="${NODE_ENV:-production}" \ --t ${APP_NAME_LOWER}-validation \ ---load \ -. - -print_success "Docker image built successfully" - -print_status "5. Testing container startup and health..." - -# Run container in background -docker run --rm -d \ --p 8083:80 \ ---name ${APP_NAME_LOWER}-validation-test \ -${APP_NAME_LOWER}-validation - -# Wait for container to start -sleep 5 - -# Check if container is running -if ! docker ps | grep -q ${APP_NAME_LOWER}-validation-test; then - print_error "Container failed to start" - docker logs ${APP_NAME_LOWER}-validation-test - exit 1 -fi - -print_success "Container started successfully" - -# Test health endpoint -print_status "5. Testing health endpoint..." -for i in {1..10}; do - if curl -s -f http://localhost:8083/health > /dev/null; then - print_success "Health endpoint responding" - break - elif [[ $i -eq 10 ]]; then - print_error "Health endpoint not responding after 10 attempts" - exit 1 - else - print_warning "Health endpoint not ready, retrying... ($i/10)" - sleep 2 - fi -done - -# Test main application -print_status "6. Testing main application..." -HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083) -if [[ $HTTP_CODE -eq 200 ]]; then - print_success "Main application responding (HTTP $HTTP_CODE)" -else - print_error "Main application not responding properly (HTTP $HTTP_CODE)" - exit 1 -fi - -# Test docker-compose build -print_status "7. Testing Docker Compose build..." -docker compose -f docker/docker-compose.yaml build frontend --no-cache - -print_success "Docker Compose build successful" - -# Test docker-compose with validation project name -print_status "8. Testing Docker Compose deployment..." -docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation up -d --build - -# Wait for services to start -sleep 10 - -# Check service health -if docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation ps | grep -q "Up"; then - print_success "Docker Compose services started successfully" -else - print_error "Docker Compose services failed to start" - docker compose -f docker/docker-compose.yaml -p ${APP_NAME_LOWER}-validation logs - exit 1 -fi - -# Test health of compose deployment -if curl -s -f http://localhost:8080/health > /dev/null; then - print_success "Docker Compose health endpoint responding" -else - print_warning "Docker Compose health endpoint not responding (may need CouchDB)" -fi - -print_status "9. Checking image size..." -IMAGE_SIZE=$(docker image inspect ${APP_NAME_LOWER}-validation --format='{{.Size}}' | numfmt --to=iec) -print_success "Image size: $IMAGE_SIZE" - -print_status "10. Validating security configuration..." - -# Check if image runs as non-root -USER_INFO=$(docker run --rm ${APP_NAME_LOWER}-validation whoami) -if [[ "$USER_INFO" != "root" ]]; then - print_success "Container runs as non-root user: $USER_INFO" -else - print_warning "Container runs as root user (security consideration)" -fi - -# Check nginx configuration -if docker run --rm ${APP_NAME_LOWER}-validation nginx -t 2>/dev/null; then - print_success "Nginx configuration is valid" -else - print_error "Nginx configuration has issues" - exit 1 -fi - -print_status "11. Final validation complete!" - -echo -echo "πŸŽ‰ Deployment validation successful!" -echo -echo "Summary:" -echo "βœ… Environment files validated" -echo "βœ… Docker image builds successfully" -echo "βœ… Container starts and runs healthy" -echo "βœ… Health endpoints respond correctly" -echo "βœ… Docker Compose deployment works" -echo "βœ… Security configuration validated" -echo "βœ… Image size optimized ($IMAGE_SIZE)" -echo -echo "Your deployment is ready for production! πŸš€" -echo -echo "Next steps:" -echo "1. Configure production environment variables in .env" -echo "2. Run './deploy.sh production' for production deployment" -echo "3. Set up monitoring and backups" -echo "4. Configure SSL/TLS certificates" -echo diff --git a/scripts/validate-env.sh b/scripts/validate-env.sh deleted file mode 100755 index 20ca405..0000000 --- a/scripts/validate-env.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/bin/bash - -# Environment validation script -# Validates all .env files for consistency and completeness - -set -e - -print_header() { - echo "πŸ” Environment Configuration Validation" - echo "======================================" - echo "" -} - -print_section() { - echo "" - echo "πŸ“‹ $1" - echo "$(printf '%*s' ${#1} '' | tr ' ' '-')" -} - -print_success() { - echo "βœ… $1" -} - -print_warning() { - echo "⚠️ $1" -} - -print_error() { - echo "❌ $1" -} - -# Required variables for each environment -CORE_VARS=( - "COUCHDB_USER" - "COUCHDB_PASSWORD" - "VITE_COUCHDB_URL" - "VITE_COUCHDB_USER" - "VITE_COUCHDB_PASSWORD" - "APP_BASE_URL" - "MAILGUN_API_KEY" - "MAILGUN_DOMAIN" - "MAILGUN_FROM_EMAIL" -) - -K8S_VARS=( - "INGRESS_HOST" -) - -OPTIONAL_VARS=( - "NODE_ENV" - "VITE_GOOGLE_CLIENT_ID" - "VITE_GITHUB_CLIENT_ID" -) - -validate_file() { - local file="$1" - local file_type="$2" - - print_section "Validating $file ($file_type)" - - if [[ ! -f "$file" ]]; then - print_error "File not found: $file" - return 1 - fi - - local missing_vars=() - local found_vars=() - - # Check core variables - for var in "${CORE_VARS[@]}"; do - if grep -q "^${var}=" "$file" || grep -q "^#.*${var}=" "$file"; then - found_vars+=("$var") - else - missing_vars+=("$var") - fi - done - - # Check K8s variables for relevant files - if [[ "$file_type" != "template" ]]; then - for var in "${K8S_VARS[@]}"; do - if grep -q "^${var}=" "$file" || grep -q "^#.*${var}=" "$file"; then - found_vars+=("$var") - else - missing_vars+=("$var") - fi - done - fi - - # Report results - print_success "Found ${#found_vars[@]} variables" - - if [[ ${#missing_vars[@]} -gt 0 ]]; then - print_warning "Missing variables:" - for var in "${missing_vars[@]}"; do - echo " - $var" - done - fi - - # Check for old VITE_MAILGUN variables - if grep -q "VITE_MAILGUN" "$file"; then - print_error "Found deprecated VITE_MAILGUN variables (should be MAILGUN_*)" - fi - - # Check variable format - local malformed_vars=() - while IFS= read -r line; do - if [[ "$line" =~ ^[A-Z_]+=.* ]]; then - local var_name="${line%%=*}" - if [[ ! "$var_name" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then - malformed_vars+=("$var_name") - fi - fi - done < "$file" - - if [[ ${#malformed_vars[@]} -gt 0 ]]; then - print_warning "Malformed variable names:" - for var in "${malformed_vars[@]}"; do - echo " - $var" - done - fi - - echo "" -} - -validate_consistency() { - print_section "Cross-file Consistency Check" - - # Extract variable names from each file - local example_vars=() - local env_vars=() - local prod_vars=() - - if [[ -f ".env.example" ]]; then - while IFS= read -r line; do - if [[ "$line" =~ ^[A-Z_]+=.* ]]; then - example_vars+=("${line%%=*}") - fi - done < ".env.example" - fi - - if [[ -f ".env" ]]; then - while IFS= read -r line; do - if [[ "$line" =~ ^[A-Z_]+=.* ]]; then - env_vars+=("${line%%=*}") - fi - done < ".env" - fi - - if [[ -f ".env.production" ]]; then - while IFS= read -r line; do - if [[ "$line" =~ ^[A-Z_]+=.* ]]; then - prod_vars+=("${line%%=*}") - fi - done < ".env.production" - fi - - # Check if .env and .env.production have all variables from .env.example - local missing_in_env=() - local missing_in_prod=() - - for var in "${example_vars[@]}"; do - if [[ ! " ${env_vars[@]} " =~ " ${var} " ]]; then - missing_in_env+=("$var") - fi - if [[ ! " ${prod_vars[@]} " =~ " ${var} " ]]; then - missing_in_prod+=("$var") - fi - done - - if [[ ${#missing_in_env[@]} -eq 0 ]]; then - print_success ".env has all variables from .env.example" - else - print_warning ".env missing variables from .env.example:" - for var in "${missing_in_env[@]}"; do - echo " - $var" - done - fi - - if [[ ${#missing_in_prod[@]} -eq 0 ]]; then - print_success ".env.production has all variables from .env.example" - else - print_warning ".env.production missing variables from .env.example:" - for var in "${missing_in_prod[@]}"; do - echo " - $var" - done - fi - - echo "" -} - -validate_k8s_template() { - print_section "Kubernetes Template Validation" - - local template_file="k8s/ingress.yaml.template" - - if [[ ! -f "$template_file" ]]; then - print_error "Template file not found: $template_file" - return 1 - fi - - # Check for template variables - local template_vars=() - while IFS= read -r line; do - if [[ "$line" =~ \$\{([A-Z_][A-Z0-9_]*)\} ]]; then - local var_name="${BASH_REMATCH[1]}" - if [[ ! " ${template_vars[@]} " =~ " ${var_name} " ]]; then - template_vars+=("$var_name") - fi - fi - done < "$template_file" - - print_success "Found ${#template_vars[@]} template variables:" - for var in "${template_vars[@]}"; do - echo " - \${$var}" - done - - # Check if template variables are defined in env files - for var in "${template_vars[@]}"; do - local found_in_files=() - - if grep -q "^${var}=" ".env.example" 2>/dev/null; then - found_in_files+=(".env.example") - fi - if grep -q "^${var}=" ".env" 2>/dev/null; then - found_in_files+=(".env") - fi - if grep -q "^${var}=" ".env.production" 2>/dev/null; then - found_in_files+=(".env.production") - fi - - if [[ ${#found_in_files[@]} -gt 0 ]]; then - print_success "$var defined in: ${found_in_files[*]}" - else - print_error "$var not defined in any environment file" - fi - done - - echo "" -} - -main() { - print_header - - # Validate individual files - if [[ -f ".env.example" ]]; then - validate_file ".env.example" "template" - fi - - if [[ -f ".env" ]]; then - validate_file ".env" "development" - fi - - if [[ -f ".env.production" ]]; then - validate_file ".env.production" "production" - fi - - # Cross-file validation - validate_consistency - - # Kubernetes template validation - validate_k8s_template - - print_section "Summary" - print_success "Environment validation complete!" - echo "" - echo "πŸ’‘ Tips:" - echo " - Copy .env.example to .env for local development" - echo " - Use .env.production for production deployments" - echo " - Run './deploy-k8s.sh --dry-run' to test Kubernetes deployment" - echo " - All Mailgun variables use server-side naming (no VITE_ prefix)" - echo "" -} - -main "$@"