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¶
- Environment Variables (Highest Priority)
- Local .env Files
- Environment-specific .env Files
- Base Configuration Files
- 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.