Skip to content

Configuration Management

This document provides comprehensive guidance for configuring the Stratpoint Timesheet Application across different environments, including development, staging, and production deployments.

Configuration Architecture

Configuration Hierarchy

graph TB
    A[Base Configuration] --> B[Environment Specific]
    B --> C[Local Overrides]

    A --> D[config/app.php]
    A --> E[config/database.php]
    A --> F[config/services.php]

    B --> G[.env.development]
    B --> H[.env.staging]
    B --> I[.env.production]

    C --> J[.env.local]
    C --> K[Docker Compose Overrides]
    C --> L[Kubernetes ConfigMaps]

Configuration Sources Priority

  1. Environment Variables (Highest Priority)
  2. Local .env Files
  3. Environment-specific .env Files
  4. Base Configuration Files
  5. Default Values (Lowest Priority)

Environment Configuration

Development Environment

# .env.development
APP_NAME="Stratpoint Timesheet (Development)"
APP_ENV=development
APP_KEY=base64:your-development-key-here
APP_DEBUG=true
APP_URL=http://localhost:8000

# Database Configuration
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=stratpoint_timesheet_dev
DB_USERNAME=dev_user
DB_PASSWORD=dev_password

# Cache Configuration
CACHE_DRIVER=redis
REDIS_HOST=localhost
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0

# Queue Configuration
QUEUE_CONNECTION=redis
QUEUE_FAILED_DRIVER=database

# Mail Configuration
MAIL_MAILER=log
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

# AWS Configuration (Development)
AWS_ACCESS_KEY_ID=dev_access_key
AWS_SECRET_ACCESS_KEY=dev_secret_key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=stratpoint-timesheet-dev

# Integration Configuration
SINOP_API_URL=https://dev-api.sinop.stratpoint.com
SINOP_API_KEY=dev_sinop_key
NETSUITE_API_URL=https://dev.netsuite.com
NETSUITE_API_TOKEN=dev_netsuite_token

# Logging
LOG_CHANNEL=stack
LOG_LEVEL=debug
LOG_SLACK_WEBHOOK_URL=null

# Session Configuration
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

Staging Environment

# .env.staging
APP_NAME="Stratpoint Timesheet (Staging)"
APP_ENV=staging
APP_KEY=base64:your-staging-key-here
APP_DEBUG=false
APP_URL=https://staging-timesheet.stratpoint.com

# Database Configuration
DB_CONNECTION=mysql
DB_HOST=staging-db.stratpoint.com
DB_PORT=3306
DB_DATABASE=stratpoint_timesheet_staging
DB_USERNAME=staging_user
DB_PASSWORD=${DB_PASSWORD_STAGING}

# Cache Configuration
CACHE_DRIVER=redis
REDIS_HOST=staging-redis.stratpoint.com
REDIS_PASSWORD=${REDIS_PASSWORD_STAGING}
REDIS_PORT=6379
REDIS_DB=0

# Queue Configuration
QUEUE_CONNECTION=redis
QUEUE_FAILED_DRIVER=database

# Mail Configuration
MAIL_MAILER=smtp
MAIL_HOST=smtp.stratpoint.com
MAIL_PORT=587
MAIL_USERNAME=${MAIL_USERNAME_STAGING}
MAIL_PASSWORD=${MAIL_PASSWORD_STAGING}
MAIL_ENCRYPTION=tls

# AWS Configuration (Staging)
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_STAGING}
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_STAGING}
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=stratpoint-timesheet-staging

# Integration Configuration
SINOP_API_URL=https://staging-api.sinop.stratpoint.com
SINOP_API_KEY=${SINOP_API_KEY_STAGING}
NETSUITE_API_URL=https://staging.netsuite.com
NETSUITE_API_TOKEN=${NETSUITE_API_TOKEN_STAGING}

# Logging
LOG_CHANNEL=stack
LOG_LEVEL=info
LOG_SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_STAGING}

# Session Configuration
SESSION_DRIVER=redis
SESSION_LIFETIME=480
SESSION_ENCRYPT=true
SESSION_PATH=/
SESSION_DOMAIN=.stratpoint.com

Production Environment

# .env.production
APP_NAME="Stratpoint Timesheet"
APP_ENV=production
APP_KEY=base64:your-production-key-here
APP_DEBUG=false
APP_URL=https://timesheet.stratpoint.com

# Database Configuration
DB_CONNECTION=mysql
DB_HOST=prod-db-cluster.stratpoint.com
DB_PORT=3306
DB_DATABASE=stratpoint_timesheet
DB_USERNAME=prod_user
DB_PASSWORD=${DB_PASSWORD_PRODUCTION}

# Read Replica Configuration
DB_READ_HOST=prod-db-read.stratpoint.com
DB_READ_USERNAME=read_user
DB_READ_PASSWORD=${DB_READ_PASSWORD_PRODUCTION}

# Cache Configuration
CACHE_DRIVER=redis
REDIS_HOST=prod-redis-cluster.stratpoint.com
REDIS_PASSWORD=${REDIS_PASSWORD_PRODUCTION}
REDIS_PORT=6379
REDIS_DB=0

# Queue Configuration
QUEUE_CONNECTION=redis
QUEUE_FAILED_DRIVER=database

# Mail Configuration
MAIL_MAILER=smtp
MAIL_HOST=smtp.stratpoint.com
MAIL_PORT=587
MAIL_USERNAME=${MAIL_USERNAME_PRODUCTION}
MAIL_PASSWORD=${MAIL_PASSWORD_PRODUCTION}
MAIL_ENCRYPTION=tls

# AWS Configuration (Production)
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_PRODUCTION}
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY_PRODUCTION}
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=stratpoint-timesheet-production

# Integration Configuration
SINOP_API_URL=https://api.sinop.stratpoint.com
SINOP_API_KEY=${SINOP_API_KEY_PRODUCTION}
NETSUITE_API_URL=https://production.netsuite.com
NETSUITE_API_TOKEN=${NETSUITE_API_TOKEN_PRODUCTION}

# Logging
LOG_CHANNEL=stack
LOG_LEVEL=warning
LOG_SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_PRODUCTION}

# Session Configuration
SESSION_DRIVER=redis
SESSION_LIFETIME=480
SESSION_ENCRYPT=true
SESSION_PATH=/
SESSION_DOMAIN=.stratpoint.com

# Security Configuration
FORCE_HTTPS=true
HSTS_MAX_AGE=31536000
CONTENT_SECURITY_POLICY=true

Application Configuration

Core Application Settings

<?php
// config/app.php

return [
    'name' => env('APP_NAME', 'Stratpoint Timesheet'),
    'env' => env('APP_ENV', 'production'),
    'debug' => env('APP_DEBUG', false),
    'url' => env('APP_URL', 'http://localhost'),
    'asset_url' => env('ASSET_URL', null),
    'timezone' => 'Asia/Manila',
    'locale' => 'en',
    'fallback_locale' => 'en',
    'faker_locale' => 'en_US',
    'key' => env('APP_KEY'),
    'cipher' => 'AES-256-CBC',

    // Application-specific settings
    'timesheet' => [
        'default_hours_per_day' => 8,
        'max_hours_per_day' => 24,
        'time_increment' => 0.25, // 15 minutes
        'auto_save_interval' => 30, // seconds
        'approval_timeout_days' => 7,
        'overtime_threshold' => 40, // hours per week
    ],

    // Feature flags
    'features' => [
        'mobile_app' => env('FEATURE_MOBILE_APP', true),
        'offline_mode' => env('FEATURE_OFFLINE_MODE', true),
        'advanced_reporting' => env('FEATURE_ADVANCED_REPORTING', true),
        'integration_sinop' => env('FEATURE_SINOP_INTEGRATION', true),
        'integration_netsuite' => env('FEATURE_NETSUITE_INTEGRATION', true),
        'ai_insights' => env('FEATURE_AI_INSIGHTS', false),
    ],

    // Security settings
    'security' => [
        'password_min_length' => 8,
        'password_require_special' => true,
        'session_timeout' => 480, // minutes
        'max_login_attempts' => 5,
        'lockout_duration' => 900, // seconds (15 minutes)
        'two_factor_required' => env('REQUIRE_2FA', false),
    ],
];

Database Configuration

<?php
// config/database.php

return [
    'default' => env('DB_CONNECTION', 'mysql'),

    'connections' => [
        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

        'mysql_read' => [
            'driver' => 'mysql',
            'host' => env('DB_READ_HOST', env('DB_HOST', '127.0.0.1')),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_READ_USERNAME', env('DB_USERNAME', 'forge')),
            'password' => env('DB_READ_PASSWORD', env('DB_PASSWORD', '')),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
    ],

    'migrations' => 'migrations',

    'redis' => [
        'client' => env('REDIS_CLIENT', 'phpredis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],
    ],
];

Services Configuration

<?php
// config/services.php

return [
    'mailgun' => [
        'domain' => env('MAILGUN_DOMAIN'),
        'secret' => env('MAILGUN_SECRET'),
        'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
    ],

    'postmark' => [
        'token' => env('POSTMARK_TOKEN'),
    ],

    'ses' => [
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    ],

    // Integration Services
    'sinop' => [
        'api_url' => env('SINOP_API_URL'),
        'api_key' => env('SINOP_API_KEY'),
        'timeout' => env('SINOP_TIMEOUT', 30),
        'retry_attempts' => env('SINOP_RETRY_ATTEMPTS', 3),
    ],

    'netsuite' => [
        'api_url' => env('NETSUITE_API_URL'),
        'api_token' => env('NETSUITE_API_TOKEN'),
        'consumer_key' => env('NETSUITE_CONSUMER_KEY'),
        'consumer_secret' => env('NETSUITE_CONSUMER_SECRET'),
        'token_id' => env('NETSUITE_TOKEN_ID'),
        'token_secret' => env('NETSUITE_TOKEN_SECRET'),
        'account_id' => env('NETSUITE_ACCOUNT_ID'),
    ],

    'csat' => [
        'api_url' => env('CSAT_API_URL'),
        'client_id' => env('CSAT_CLIENT_ID'),
        'client_secret' => env('CSAT_CLIENT_SECRET'),
    ],

    'f1' => [
        'api_url' => env('F1_API_URL'),
        'api_key' => env('F1_API_KEY'),
        'secret_key' => env('F1_SECRET_KEY'),
    ],

    'wookie' => [
        'api_url' => env('WOOKIE_API_URL'),
        'client_id' => env('WOOKIE_CLIENT_ID'),
        'client_secret' => env('WOOKIE_CLIENT_SECRET'),
    ],

    'timedef' => [
        'api_url' => env('TIMEDEF_API_URL'),
        'api_token' => env('TIMEDEF_API_TOKEN'),
    ],
];

Docker Configuration

Docker Compose for Development

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development
    ports:
      - "8000:8000"
    volumes:
      - .:/var/www/html
      - ./storage:/var/www/html/storage
    environment:
      - APP_ENV=development
      - DB_HOST=mysql
      - REDIS_HOST=redis
    depends_on:
      - mysql
      - redis
    networks:
      - timesheet-network

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: stratpoint_timesheet_dev
      MYSQL_USER: dev_user
      MYSQL_PASSWORD: dev_password
    volumes:
      - mysql_data:/var/lib/mysql
      - ./database/init:/docker-entrypoint-initdb.d
    networks:
      - timesheet-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - timesheet-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/sites:/etc/nginx/sites-available
      - ./public:/var/www/html/public
    depends_on:
      - app
    networks:
      - timesheet-network

volumes:
  mysql_data:
  redis_data:

networks:
  timesheet-network:
    driver: bridge

Production Docker Configuration

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    environment:
      - APP_ENV=production
    env_file:
      - .env.production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    networks:
      - timesheet-network
      - database-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/nginx.prod.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/ssl:/etc/nginx/ssl
    deploy:
      replicas: 2
    networks:
      - timesheet-network

networks:
  timesheet-network:
    external: true
  database-network:
    external: true

Kubernetes Configuration

ConfigMap for Application Settings

# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: timesheet-config
  namespace: stratpoint
data:
  APP_NAME: "Stratpoint Timesheet"
  APP_ENV: "production"
  APP_DEBUG: "false"
  APP_URL: "https://timesheet.stratpoint.com"
  DB_CONNECTION: "mysql"
  DB_HOST: "mysql-service"
  DB_PORT: "3306"
  DB_DATABASE: "stratpoint_timesheet"
  CACHE_DRIVER: "redis"
  REDIS_HOST: "redis-service"
  REDIS_PORT: "6379"
  QUEUE_CONNECTION: "redis"
  LOG_CHANNEL: "stack"
  LOG_LEVEL: "info"

Secret for Sensitive Configuration

# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: timesheet-secrets
  namespace: stratpoint
type: Opaque
data:
  APP_KEY: <base64-encoded-app-key>
  DB_PASSWORD: <base64-encoded-db-password>
  REDIS_PASSWORD: <base64-encoded-redis-password>
  AWS_ACCESS_KEY_ID: <base64-encoded-aws-key>
  AWS_SECRET_ACCESS_KEY: <base64-encoded-aws-secret>
  SINOP_API_KEY: <base64-encoded-sinop-key>
  NETSUITE_API_TOKEN: <base64-encoded-netsuite-token>

Configuration Management Best Practices

Environment Variable Management

#!/bin/bash
# scripts/setup-environment.sh

set -e

ENVIRONMENT=${1:-development}

echo "Setting up environment: $ENVIRONMENT"

# Copy environment-specific configuration
cp ".env.$ENVIRONMENT" .env

# Generate application key if not exists
if ! grep -q "APP_KEY=base64:" .env; then
    php artisan key:generate
fi

# Set up database
php artisan migrate --force

# Clear and cache configuration
php artisan config:clear
php artisan config:cache

# Set up storage links
php artisan storage:link

# Install dependencies
composer install --optimize-autoloader --no-dev

# Build frontend assets
npm ci
npm run production

echo "Environment setup complete for: $ENVIRONMENT"

Configuration Validation

<?php
// app/Console/Commands/ValidateConfiguration.php

class ValidateConfiguration extends Command
{
    protected $signature = 'config:validate {environment?}';
    protected $description = 'Validate application configuration';

    public function handle()
    {
        $environment = $this->argument('environment') ?? config('app.env');

        $this->info("Validating configuration for environment: $environment");

        $validations = [
            'validateDatabaseConnection',
            'validateRedisConnection',
            'validateAwsConfiguration',
            'validateIntegrationServices',
            'validateSecuritySettings',
        ];

        $errors = [];

        foreach ($validations as $validation) {
            try {
                $this->$validation();
                $this->line("✓ " . str_replace('validate', '', $validation));
            } catch (Exception $e) {
                $errors[] = $e->getMessage();
                $this->error("✗ " . str_replace('validate', '', $validation) . ": " . $e->getMessage());
            }
        }

        if (empty($errors)) {
            $this->info("All configuration validations passed!");
            return 0;
        } else {
            $this->error("Configuration validation failed with " . count($errors) . " errors.");
            return 1;
        }
    }

    private function validateDatabaseConnection()
    {
        DB::connection()->getPdo();
    }

    private function validateRedisConnection()
    {
        Redis::ping();
    }

    private function validateAwsConfiguration()
    {
        if (!config('services.ses.key') || !config('services.ses.secret')) {
            throw new Exception("AWS credentials not configured");
        }
    }

    private function validateIntegrationServices()
    {
        $services = ['sinop', 'netsuite', 'csat', 'f1', 'wookie', 'timedef'];

        foreach ($services as $service) {
            if (!config("services.$service.api_url")) {
                throw new Exception("$service API URL not configured");
            }
        }
    }

    private function validateSecuritySettings()
    {
        if (config('app.env') === 'production') {
            if (config('app.debug')) {
                throw new Exception("Debug mode should be disabled in production");
            }

            if (!config('app.key')) {
                throw new Exception("Application key not set");
            }
        }
    }
}

This comprehensive configuration management system ensures consistent, secure, and maintainable deployments across all environments while providing validation and monitoring capabilities.