From a7f578ca3d2a358abd7cc041334623514cb50052 Mon Sep 17 00:00:00 2001 From: arifal Date: Thu, 26 Jun 2025 18:28:26 +0700 Subject: [PATCH] add docker for server demo --- Dockerfile | 10 +- OPTIMIZED_TABLE_STRUCTURE.md | 210 --------- README-Docker-Usage.md | 109 ----- README.md | 191 +++++--- app/Console/Commands/SyncGoogleSheetData.php | 9 +- docker-compose.local.yml | 14 +- docker-compose.yml | 216 +++++++-- docker/mysql/conf.d/production.cnf | 55 +++ docker/nginx/Dockerfile | 32 ++ docker/nginx/conf.d/production.conf | 113 +++++ docker/nginx/conf.d/reverse-proxy.conf | 137 ++++++ docker/nginx/conf.d/sibedas-internal.conf | 58 +++ docker/nginx/ssl-setup.sh | 123 +++++ docker/nginx/ssl/.gitignore | 14 + docker/supervisor/laravel-production.conf | 12 +- docs/README.md | 449 +++++++++++++++++++ env.production.example | 154 +++++++ build-and-zip.sh => scripts/build-and-zip.sh | 0 scripts/deploy-production.sh | 226 ++++++++++ scripts/import-sibedas-database.sh | 257 +++++++++++ scripts/setup-local.sh | 188 ++++++++ scripts/setup-reverse-proxy.sh | 129 ++++++ scripts/setup-ssl.sh | 145 ++++++ 23 files changed, 2420 insertions(+), 431 deletions(-) delete mode 100644 OPTIMIZED_TABLE_STRUCTURE.md delete mode 100644 README-Docker-Usage.md create mode 100644 docker/mysql/conf.d/production.cnf create mode 100644 docker/nginx/Dockerfile create mode 100644 docker/nginx/conf.d/production.conf create mode 100644 docker/nginx/conf.d/reverse-proxy.conf create mode 100644 docker/nginx/conf.d/sibedas-internal.conf create mode 100644 docker/nginx/ssl-setup.sh create mode 100644 docker/nginx/ssl/.gitignore create mode 100644 docs/README.md create mode 100644 env.production.example rename build-and-zip.sh => scripts/build-and-zip.sh (100%) create mode 100755 scripts/deploy-production.sh create mode 100755 scripts/import-sibedas-database.sh create mode 100755 scripts/setup-local.sh create mode 100755 scripts/setup-reverse-proxy.sh create mode 100755 scripts/setup-ssl.sh diff --git a/Dockerfile b/Dockerfile index afe11ba..c64d04d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,7 @@ WORKDIR /var/www # Install PHP extensions RUN apt-get update && apt-get install -y \ git curl zip unzip libpng-dev libonig-dev libxml2-dev libzip-dev \ + supervisor \ && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip # Install Node.js @@ -94,8 +95,15 @@ RUN php artisan config:clear \ RUN php artisan storage:link +# Create supervisor directories +RUN mkdir -p /var/log/supervisor /var/run/supervisor + +# Copy supervisor configuration +COPY docker/supervisor/supervisord.conf /etc/supervisor/supervisord.conf +COPY docker/supervisor/laravel-production.conf /etc/supervisor/conf.d/laravel-production.conf + # Permissions RUN chown -R www-data:www-data /var/www && chmod -R 755 /var/www/storage /var/www/public EXPOSE 9000 -CMD ["php-fpm"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/OPTIMIZED_TABLE_STRUCTURE.md b/OPTIMIZED_TABLE_STRUCTURE.md deleted file mode 100644 index bcf9623..0000000 --- a/OPTIMIZED_TABLE_STRUCTURE.md +++ /dev/null @@ -1,210 +0,0 @@ -# Struktur Tabel Retribusi PBG yang Dioptimalkan - -## Ringkasan Optimasi - -Struktur tabel baru ini **lebih sederhana**, **fokus pada perhitungan**, dan **menghilangkan redundansi** dari struktur sebelumnya. - -## Perbandingan Struktur - -### SEBELUM (Kompleks) - -- `building_functions` - 8 kolom + relationship kompleks -- `building_function_parameters` - 12 kolom dengan mismatch model/migration -- `retribution_formulas` - Menyimpan formula sebagai string -- `retribution_proposals` - 15+ kolom dengan banyak redundansi -- `floor_height_indices` - OK, tidak berubah - -### SESUDAH (Sederhana) - -- `building_types` - **7 kolom**, hierarki sederhana -- `retribution_indices` - **6 kolom**, parameter calculation saja -- `height_indices` - **3 kolom**, sama seperti sebelumnya -- `retribution_configs` - **5 kolom**, konfigurasi global -- `retribution_calculations` - **8 kolom**, hasil perhitungan saja - ---- - -## Detail Struktur Tabel Baru - -### 1. `building_types` - -**Fungsi:** Menyimpan jenis fungsi bangunan dengan hierarki sederhana - -| Kolom | Tipe | Keterangan | -| ------------- | ------------ | ---------------------------------- | -| `id` | bigint | Primary key | -| `code` | varchar(10) | Kode unik (UMKM, KEAGAMAAN, dll) | -| `name` | varchar(100) | Nama fungsi bangunan | -| `parent_id` | bigint | ID parent (untuk hierarki) | -| `level` | tinyint | Level hierarki (1=parent, 2=child) | -| `coefficient` | decimal(8,4) | **Koefisien untuk perhitungan** | -| `is_free` | boolean | **Apakah gratis (keagamaan, MBR)** | - -### 2. `retribution_indices` - -**Fungsi:** Menyimpan parameter indeks untuk perhitungan (1:1 dengan building_types) - -| Kolom | Tipe | Keterangan | -| ----------------------- | ------------ | ---------------------------------- | -| `id` | bigint | Primary key | -| `building_type_id` | bigint | FK ke building_types | -| `ip_permanent` | decimal(8,4) | **Indeks Permanensi** | -| `ip_complexity` | decimal(8,4) | **Indeks Kompleksitas** | -| `locality_index` | decimal(8,4) | **Indeks Lokalitas** | -| `infrastructure_factor` | decimal(8,4) | **Faktor prasarana (default 50%)** | - -### 3. `height_indices` - -**Fungsi:** Indeks ketinggian per lantai (sama seperti sebelumnya) - -| Kolom | Tipe | Keterangan | -| -------------- | ------------ | ---------------------------------------- | -| `id` | bigint | Primary key | -| `floor_number` | tinyint | Nomor lantai (1,2,3,4,5,6) | -| `height_index` | decimal(8,6) | **IP Ketinggian (1.0, 1.09, 1.12, dst)** | - -### 4. `retribution_configs` - -**Fungsi:** Konfigurasi global untuk perhitungan (menggantikan hard-coded values) - -| Kolom | Tipe | Keterangan | -| ------------- | ------------- | --------------------- | -| `id` | bigint | Primary key | -| `key` | varchar(50) | Kunci konfigurasi | -| `value` | decimal(15,2) | **Nilai konfigurasi** | -| `description` | varchar(200) | Deskripsi | - -**Data yang disimpan:** - -- `BASE_VALUE` = 70350 (nilai dasar) -- `INFRASTRUCTURE_MULTIPLIER` = 0.5 (50% prasarana) -- `HEIGHT_MULTIPLIER` = 0.5 (pengali indeks ketinggian) - -### 5. `retribution_calculations` - -**Fungsi:** Hasil perhitungan retribusi (history) - -| Kolom | Tipe | Keterangan | -| -------------------- | ------------- | -------------------------------- | -| `id` | bigint | Primary key | -| `calculation_id` | varchar(20) | ID unik perhitungan | -| `building_type_id` | bigint | FK ke building_types | -| `floor_number` | tinyint | Lantai yang dipilih | -| `building_area` | decimal(12,2) | **Luas bangunan input** | -| `retribution_amount` | decimal(15,2) | **Hasil perhitungan** | -| `calculation_detail` | json | **Detail breakdown perhitungan** | -| `calculated_at` | timestamp | Waktu perhitungan | - ---- - -## Formula Perhitungan - -### Formula Excel yang Diimplementasikan: - -``` -H13 = coefficient * (ip_permanent + ip_complexity + (0.5 * height_index)) - -Main Calculation = building_area * (locality_index * BASE_VALUE * H13) - -Infrastructure = INFRASTRUCTURE_MULTIPLIER * Main Calculation - -Total Retribution = Main Calculation + Infrastructure -``` - -### Implementasi dalam Service: - -```php -// Step 1: Calculate H13 coefficient -$h13 = $buildingType->coefficient * ( - $indices->ip_permanent + - $indices->ip_complexity + - (0.5 * $heightIndex) -); - -// Step 2: Main calculation -$mainCalculation = $buildingArea * ($indices->locality_index * $baseValue * $h13); - -// Step 3: Infrastructure (50% additional) -$infrastructureCalculation = 0.5 * $mainCalculation; - -// Step 4: Total -$totalRetribution = $mainCalculation + $infrastructureCalculation; -``` - ---- - -## Keuntungan Struktur Baru - -### ✅ **Simplicity** - -- **5 tabel** vs 8+ tabel sebelumnya -- **Kolom minimal** hanya yang diperlukan untuk perhitungan -- **No redundant data** seperti ip_ketinggian di proposals - -### ✅ **Performance** - -- **Proper indexes** untuk query yang sering digunakan -- **Normalized structure** mengurangi storage -- **Cached configs** untuk values yang jarang berubah - -### ✅ **Maintainability** - -- **Clear separation** antara master data dan calculation results -- **Configurable values** tidak hard-coded lagi -- **Single responsibility** setiap tabel punya tujuan jelas - -### ✅ **Flexibility** - -- **Easy to extend** untuk fungsi bangunan baru -- **Configurable formulas** lewat RetributionConfig -- **Audit trail** lewat calculation history - -### ✅ **Data Integrity** - -- **Proper constraints** untuk validasi data -- **Foreign key relationships** yang benar -- **No model-migration mismatch** - ---- - -## Migration Guide - -### Langkah Implementasi: - -1. **Run Migration:** `php artisan migrate` untuk tabel baru -2. **Seed Data:** Data master berdasarkan Excel akan otomatis ter-seed -3. **Update Code:** Ganti penggunaan model lama dengan model baru -4. **Test Calculation:** Verifikasi hasil perhitungan sama dengan Excel -5. **Deploy:** Struktur siap production - -### Data Migration (Optional): - -Jika ada data existing di tabel lama yang perlu dipindahkan, buat script migration untuk transfer data dari struktur lama ke struktur baru. - ---- - -## Usage Example - -```php -// Initialize service -$calculator = new RetributionCalculatorService(); - -// Calculate retribution -$result = $calculator->calculate( - buildingTypeId: 8, // UMKM - floorNumber: 2, // 2 lantai - buildingArea: 100.50, // 100.5 m2 - saveResult: true // Simpan ke database -); - -// Result structure -[ - 'building_type' => [...], - 'total_retribution' => 31658.25, - 'formatted_amount' => 'Rp 31,658.25', - 'calculation_steps' => [...], - 'calculation_id' => 'RTB-20250130140530-123' -] -``` - -Struktur ini **jauh lebih clean**, **mudah dipahami**, dan **optimal untuk perhitungan retribusi PBG**! diff --git a/README-Docker-Usage.md b/README-Docker-Usage.md deleted file mode 100644 index 236fe02..0000000 --- a/README-Docker-Usage.md +++ /dev/null @@ -1,109 +0,0 @@ -# Docker Usage Guide - -Proyek ini memiliki dua konfigurasi Docker untuk keperluan yang berbeda: - -## 📁 File Konfigurasi - -### 1. `docker-compose.yml` - Production/Staging - -- **Target**: `production` (optimized build) -- **Environment**: Production settings -- **Debug**: Disabled -- **Asset handling**: Built dan optimized -- **Use case**: Deploy ke server production/staging - -### 2. `docker-compose.local.yml` - Local Development - -- **Target**: `local` (development build) -- **Environment**: Local development settings -- **Debug**: Enabled -- **Asset handling**: Vite dev server terpisah -- **Use case**: Development di local machine - -## 🚀 Cara Penggunaan - -### Local Development (Recommended) - -```bash -# Setup awal (otomatis) -./setup-local.sh - -# Atau manual -docker-compose -f docker-compose.local.yml up -d --build -``` - -### Production/Staging - -```bash -# Dengan environment variables -docker-compose up -d --build - -# Atau dengan custom .env -APP_ENV=production APP_DEBUG=false docker-compose up -d --build -``` - -## 🔧 Perbedaan Utama - -| Aspek | Local Development | Production | -| ---------------- | -------------------------- | --------------------- | -| **File** | `docker-compose.local.yml` | `docker-compose.yml` | -| **Target** | `local` | `production` | -| **Debug** | Enabled | Disabled | -| **Assets** | Vite dev server | Pre-built & optimized | -| **Permissions** | User mapping | Standard www-data | -| **Hot Reload** | ✅ Available | ❌ Not needed | -| **Node Modules** | Kept for dev | Removed after build | - -## 📋 Command Cheat Sheet - -### Local Development - -```bash -# Start -docker-compose -f docker-compose.local.yml up -d - -# Stop -docker-compose -f docker-compose.local.yml down - -# Logs -docker-compose -f docker-compose.local.yml logs -f app - -# Execute commands -docker-compose -f docker-compose.local.yml exec app php artisan [command] -``` - -### Production - -```bash -# Start -docker-compose up -d - -# Stop -docker-compose down - -# Logs -docker-compose logs -f app - -# Execute commands -docker-compose exec app php artisan [command] -``` - -## 🛠 Development Workflow - -1. **Daily development**: Gunakan `docker-compose.local.yml` -2. **Testing production build**: Gunakan `docker-compose.yml` -3. **Deploy**: Gunakan `docker-compose.yml` di server - -## ❓ FAQ - -**Q: Apakah perlu kedua file?** -A: Ya, keduanya memiliki fungsi berbeda: - -- `docker-compose.local.yml` untuk development -- `docker-compose.yml` untuk production/staging - -**Q: File mana yang digunakan untuk development?** -A: Gunakan `docker-compose.local.yml` dengan menjalankan `./setup-local.sh` - -**Q: Bagaimana cara switch antara development dan production?** -A: Gunakan flag `-f` untuk specify file yang ingin digunakan diff --git a/README.md b/README.md index 27f1bcc..a1f12c6 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,167 @@ -# Usage icon +# Sibedas PBG Web -search or pick icon in here +Aplikasi web untuk manajemen data PBG (Pendidikan Berkelanjutan Guru) dengan fitur integrasi Google Sheets. -# Set up queue for running automatically +## 🚀 Quick Start -- Install Supervisor +### Prerequisites -``` -sudo apt update && sudo apt install supervisor -y +- Docker & Docker Compose +- Domain name (untuk production) + +### Local Development + +```bash +git clone +cd sibedas-pbg-web +./scripts/setup-local.sh +# Access: http://localhost:8000 ``` -- Create Supervisor Config +### Production Deployment -``` -sudo nano /etc/supervisor/conf.d/laravel-worker.conf +```bash +# 1. Setup environment +cp env.production.example .env +nano .env -[program:laravel-worker] -process_name=%(program_name)s_%(process_num)02d -command=php /home/arifal/development/sibedas-pbg-web/artisan queue:work --queue=default --timeout=82800 --tries=1 -autostart=true -autorestart=true -numprocs=1 -redirect_stderr=true -stdout_logfile=/home/arifal/development/sibedas-pbg-web/storage/logs/worker.log -stopasgroup=true -killasgroup=true +# 2. Deploy with SSL (Recommended) +./scripts/setup-reverse-proxy.sh setup + +# 3. Check status +./scripts/setup-reverse-proxy.sh status +# Access: https://yourdomain.com ``` -- Reload Supervisor +## 🏗️ Architecture + +### Local Development ``` -sudo supervisorctl reread -sudo supervisorctl update -sudo supervisorctl start laravel-worker -sudo supervisorctl restart laravel-worker -sudo supervisorctl status +Browser → Port 8000 → Nginx → PHP-FPM → MariaDB ``` -# How to running - -- Install composer package +### Production dengan Reverse Proxy ``` -composer install +Internet → Reverse Proxy (80/443) → Internal Nginx → PHP-FPM → MariaDB ``` -- Install npm package +## 🔧 Configuration -``` -npm install && npm run build +### Environment Variables + +```bash +# Domain & SSL +DOMAIN=sibedas.yourdomain.com +EMAIL=admin@yourdomain.com +SSL_TYPE=self-signed # atau letsencrypt + +# Database +DB_PASSWORD=your_secure_password +MYSQL_ROOT_PASSWORD=your_root_password + +# Laravel +APP_KEY=base64:your_app_key_here +APP_URL=https://sibedas.yourdomain.com ``` -- Create symlinks storage +## 🚀 Production Deployment Steps -``` -php artisan storage:link +### 1. Server Preparation + +```bash +# Install Docker & Docker Compose +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +sudo usermod -aG docker $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 ``` -- Running migration +### 2. Clone & Setup -``` -php artisan migrate +```bash +git clone +cd sibedas-pbg-web +chmod +x scripts/*.sh +cp env.production.example .env +nano .env ``` -- Running seeder +### 3. Deploy -``` -php artisan db:seed +```bash +# Full deployment with SSL +./scripts/setup-reverse-proxy.sh setup + +# Or step by step +./scripts/deploy-production.sh deploy +./scripts/setup-ssl.sh letsencrypt ``` -- Create view table -- excute all sql queries on folder database/view_query +### 4. Verify -# Add ENV variable - -- API_KEY_GOOGLE - -``` -Get api key from google developer console for and turn on spreadsheet api or feaature for google sheet +```bash +docker-compose ps +./scripts/setup-reverse-proxy.sh status +curl -f http://localhost/health-check ``` -- SPREAD_SHEET_ID +## 📊 Monitoring -``` -Get spreadsheet id from google sheet link +```bash +# Check status +./scripts/setup-reverse-proxy.sh status + +# View logs +docker-compose logs [service] + +# Check SSL certificate +./scripts/setup-ssl.sh check ``` -- OPENAI_API_KEY +## 🛠️ Common Commands -``` -Get OpenAI API key from chatgpt subscription +```bash +# Start services +docker-compose up -d + +# Stop services +docker-compose down + +# Restart services +docker-compose restart + +# Execute Laravel commands +docker-compose exec app php artisan [command] + +# Backup database +docker exec sibedas_db mysqldump -u root -p sibedas > backup.sql ``` -- ENV +## 📁 Scripts -``` +### Essential Scripts -API_KEY_GOOGLE="xxxxx" -SPREAD_SHEET_ID="xxxxx" -OPENAI_API_KEY="xxxxx" +- `scripts/setup-reverse-proxy.sh` - Setup lengkap reverse proxy dan SSL +- `scripts/deploy-production.sh` - Deployment production +- `scripts/setup-ssl.sh` - Setup SSL certificates -``` +### Optional Scripts -# Technology version +- `scripts/setup-local.sh` - Setup local development +- `scripts/import-sibedas-database.sh` - Manual database import -- php 8.3 -- Laravel 11 -- node v22.13.0 -- npm 10.9.2 -- mariadb Ver 15.1 Distrib 10.6.18-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper -- Ubuntu 24.04 +## 📚 Documentation + +Untuk dokumentasi lengkap, lihat [docs/README.md](docs/README.md) + +## 🆘 Support + +1. Check logs: `docker-compose logs [service]` +2. Check status: `./scripts/setup-reverse-proxy.sh status` +3. Restart services: `docker-compose restart` +4. Review documentation di folder `docs/` diff --git a/app/Console/Commands/SyncGoogleSheetData.php b/app/Console/Commands/SyncGoogleSheetData.php index 5d618fd..22e7792 100644 --- a/app/Console/Commands/SyncGoogleSheetData.php +++ b/app/Console/Commands/SyncGoogleSheetData.php @@ -54,10 +54,17 @@ class SyncGoogleSheetData extends Command $result = $service->sync_leader_data(); $this->info('✅ Leader data synchronized successfully!'); $this->table(['Section', 'Total', 'Nominal'], collect($result)->map(function($item, $key) { + // Convert nominal to numeric before formatting + $nominal = $item['nominal'] ?? 0; + if (is_string($nominal)) { + // Remove dots and convert to float + $nominal = (float) str_replace('.', '', $nominal); + } + return [ $key, $item['total'] ?? 'N/A', - number_format($item['nominal'] ?? 0, 0, ',', '.') + number_format((float) $nominal, 0, ',', '.') ]; })->toArray()); break; diff --git a/docker-compose.local.yml b/docker-compose.local.yml index d92512b..efd135b 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -24,7 +24,7 @@ services: depends_on: - db networks: - - sibedas_net + - sibedas_network_local # Add user mapping for permission compatibility user: "1000:1000" @@ -40,7 +40,7 @@ services: depends_on: - app networks: - - sibedas_net + - sibedas_network_local db: image: mariadb:10.6 @@ -54,10 +54,10 @@ services: ports: - "3306:3306" volumes: - - dbdata_local:/var/lib/mysql + - sibedas_dbdata_local:/var/lib/mysql - ./sibedas.sql:/docker-entrypoint-initdb.d/sibedas.sql networks: - - sibedas_net + - sibedas_network_local vite: build: @@ -74,11 +74,11 @@ services: ports: - "5173:5173" networks: - - sibedas_net + - sibedas_network_local volumes: - dbdata_local: + sibedas_dbdata_local: networks: - sibedas_net: + sibedas_network_local: driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index 2d02000..b65d0fd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,5 @@ services: + # Sibedas Application Container (Internal) app: build: context: . @@ -7,57 +8,214 @@ services: container_name: sibedas_app restart: unless-stopped environment: - APP_ENV: production - APP_DEBUG: false - APP_URL: ${APP_URL:-http://localhost:8000} - VITE_APP_URL: ${VITE_APP_URL:-http://localhost:8000} - DB_CONNECTION: mariadb + APP_ENV: ${APP_ENV:-production} + APP_DEBUG: ${APP_DEBUG:-false} + APP_KEY: ${APP_KEY} + APP_URL: ${APP_URL:-https://sibedas.yourdomain.com} + VITE_APP_URL: ${VITE_APP_URL:-https://sibedas.yourdomain.com} + + # Database Configuration + DB_CONNECTION: ${DB_CONNECTION:-mariadb} DB_HOST: db DB_PORT: 3306 DB_DATABASE: ${DB_DATABASE:-sibedas} - DB_USERNAME: ${DB_USERNAME:-root} - DB_PASSWORD: ${DB_PASSWORD:-root} - volumes: - - .:/var/www - depends_on: - - db - networks: - - sibedas_net + DB_USERNAME: ${DB_USERNAME:-sibedas_user} + DB_PASSWORD: ${DB_PASSWORD} - nginx: + # Cache Configuration (using database) + CACHE_DRIVER: ${CACHE_DRIVER:-database} + + # Session Configuration (using database) + SESSION_DRIVER: ${SESSION_DRIVER:-database} + SESSION_LIFETIME: ${SESSION_LIFETIME:-120} + + # Queue Configuration (using database) + QUEUE_CONNECTION: ${QUEUE_CONNECTION:-database} + + # Mail Configuration + MAIL_MAILER: ${MAIL_MAILER:-smtp} + MAIL_HOST: ${MAIL_HOST} + MAIL_PORT: ${MAIL_PORT:-587} + MAIL_USERNAME: ${MAIL_USERNAME} + MAIL_PASSWORD: ${MAIL_PASSWORD} + MAIL_ENCRYPTION: ${MAIL_ENCRYPTION:-tls} + MAIL_FROM_ADDRESS: ${MAIL_FROM_ADDRESS} + MAIL_FROM_NAME: ${MAIL_FROM_NAME:-"Sibedas"} + + # Google Sheets API + SPREAD_SHEET_ID: ${SPREAD_SHEET_ID} + volumes: + # Only mount specific directories for production security + - sibedas_app_storage:/var/www/storage + - sibedas_app_bootstrap_cache:/var/www/bootstrap/cache + - ./public:/var/www/public:ro + - ./docker/supervisor:/etc/supervisor/conf.d:ro + depends_on: + db: + condition: service_healthy + networks: + - sibedas_network + healthcheck: + test: ["CMD", "php", "-v"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + deploy: + resources: + limits: + memory: 1G + cpus: "1.0" + reservations: + memory: 512M + cpus: "0.5" + # Use Supervisor for queue and scheduler + command: + ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] + + # Internal Nginx for Sibedas App + nginx-internal: image: nginx:alpine - container_name: sibedas_nginx + container_name: sibedas_nginx_internal + restart: unless-stopped + volumes: + - ./public:/var/www/public:ro + - ./docker/nginx/conf.d/sibedas-internal.conf:/etc/nginx/conf.d/default.conf:ro + - sibedas_nginx_internal_logs:/var/log/nginx + depends_on: + app: + condition: service_healthy + networks: + - sibedas_network + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost/health-check", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + deploy: + resources: + limits: + memory: 128M + cpus: "0.25" + + # Reverse Proxy Nginx (Main Entry Point) + nginx-proxy: + build: + context: ./docker/nginx + dockerfile: Dockerfile + container_name: sibedas_nginx_proxy restart: unless-stopped ports: - - "8000:80" + - "${NGINX_HTTP_PORT:-80}:80" + - "${NGINX_HTTPS_PORT:-443}:443" + environment: + DOMAIN: ${DOMAIN:-sibedas.yourdomain.com} + EMAIL: ${EMAIL:-admin@yourdomain.com} + SSL_TYPE: ${SSL_TYPE:-self-signed} volumes: - - .:/var/www - - ./docker/nginx/conf.d/app.conf:/etc/nginx/conf.d/default.conf + - sibedas_nginx_proxy_logs:/var/log/nginx + - sibedas_ssl_certs:/etc/nginx/ssl + - sibedas_letsencrypt:/etc/letsencrypt depends_on: - - app + nginx-internal: + condition: service_healthy networks: - - sibedas_net + - sibedas_network + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost/health-check", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + deploy: + resources: + limits: + memory: 256M + cpus: "0.5" db: image: mariadb:10.6 container_name: sibedas_db restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${DB_DATABASE:-sibedas} - MYSQL_USER: ${DB_USERNAME:-root} - MYSQL_PASSWORD: ${DB_PASSWORD:-root} + MYSQL_USER: ${DB_USERNAME:-sibedas_user} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_INNODB_BUFFER_POOL_SIZE: ${MYSQL_INNODB_BUFFER_POOL_SIZE:-1G} ports: - - "3306:3306" + # Only expose if needed for external access + - "${DB_EXTERNAL_PORT:-3306}:3306" volumes: - - dbdata:/var/lib/mysql - - ./sibedas.sql:/docker-entrypoint-initdb.d/sibedas.sql + - sibedas_dbdata:/var/lib/mysql + - ./sibedas.sql:/docker-entrypoint-initdb.d/sibedas.sql:ro + - ./docker/mysql/conf.d:/etc/mysql/conf.d:ro + - sibedas_db_logs:/var/log/mysql networks: - - sibedas_net + - sibedas_network + healthcheck: + test: + [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "-u", + "${DB_USERNAME:-sibedas_user}", + "-p${DB_PASSWORD}", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + deploy: + resources: + limits: + memory: 2G + cpus: "1.0" + reservations: + memory: 1G + cpus: "0.5" volumes: - dbdata: + sibedas_dbdata: + driver: local + sibedas_app_storage: + driver: local + sibedas_app_bootstrap_cache: + driver: local + sibedas_nginx_internal_logs: + driver: local + sibedas_nginx_proxy_logs: + driver: local + sibedas_db_logs: + driver: local + sibedas_ssl_certs: + driver: local + sibedas_letsencrypt: + driver: local networks: - sibedas_net: + sibedas_network: driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 diff --git a/docker/mysql/conf.d/production.cnf b/docker/mysql/conf.d/production.cnf new file mode 100644 index 0000000..64f0586 --- /dev/null +++ b/docker/mysql/conf.d/production.cnf @@ -0,0 +1,55 @@ +[mysqld] +# Basic Settings +default-storage-engine = innodb +sql-mode = "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" +max_connections = 200 +max_user_connections = 180 + +# Character Set +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci + +# InnoDB Settings +innodb_buffer_pool_size = 1G +innodb_buffer_pool_instances = 4 +innodb_log_file_size = 256M +innodb_log_buffer_size = 64M +innodb_flush_log_at_trx_commit = 2 +innodb_flush_method = O_DIRECT +innodb_file_per_table = 1 +innodb_open_files = 400 + +# Query Cache (disabled in MySQL 8.0+, but kept for compatibility) +query_cache_type = OFF +query_cache_size = 0 + +# Temp Tables +tmp_table_size = 64M +max_heap_table_size = 64M + +# Logging +slow_query_log = 1 +slow_query_log_file = /var/log/mysql/slow.log +long_query_time = 2 +log_queries_not_using_indexes = 1 + +# Binary Logging +log-bin = mysql-bin +binlog_format = ROW +expire_logs_days = 7 +max_binlog_size = 100M + +# Safety +max_allowed_packet = 64M +bind-address = 0.0.0.0 + +# Performance Schema +performance_schema = ON +performance_schema_max_table_instances = 400 +performance_schema_max_table_handles = 4000 + +[mysql] +default-character-set = utf8mb4 + +[client] +default-character-set = utf8mb4 \ No newline at end of file diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile new file mode 100644 index 0000000..ec1eeac --- /dev/null +++ b/docker/nginx/Dockerfile @@ -0,0 +1,32 @@ +FROM nginx:alpine + +# Install required packages +RUN apk add --no-cache \ + openssl \ + certbot \ + certbot-nginx \ + bash + +# Create SSL directory +RUN mkdir -p /etc/nginx/ssl + +# Copy SSL certificates (if they exist) +COPY ssl/ /etc/nginx/ssl/ + +# Copy Nginx configuration +COPY conf.d/ /etc/nginx/conf.d/ + +# Create log directories +RUN mkdir -p /var/log/nginx + +# Copy SSL setup script +COPY ssl-setup.sh /usr/local/bin/ssl-setup.sh +RUN chmod +x /usr/local/bin/ssl-setup.sh + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD wget --quiet --tries=1 --spider http://localhost/health-check || exit 1 + +EXPOSE 80 443 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docker/nginx/conf.d/production.conf b/docker/nginx/conf.d/production.conf new file mode 100644 index 0000000..11c2f3a --- /dev/null +++ b/docker/nginx/conf.d/production.conf @@ -0,0 +1,113 @@ +server { + listen 80; + server_name yourdomain.com www.yourdomain.com; + + # Redirect HTTP to HTTPS + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name yourdomain.com www.yourdomain.com; + + root /var/www/public; + index index.php index.html index.htm; + + # SSL Configuration + ssl_certificate /etc/nginx/ssl/certificate.crt; + ssl_certificate_key /etc/nginx/ssl/private.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Security Headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Gzip Compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied expired no-cache no-store private auth; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/x-javascript + application/xml+rss + application/javascript + application/json + application/xml + image/svg+xml; + + # Client Max Body Size + client_max_body_size 100M; + + # Health check endpoint + location /health-check { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # Handle static files + location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + try_files $uri =404; + } + + # Deny access to sensitive files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + location ~ ^/(\.env|\.git|composer\.(json|lock)|package\.(json|lock)|webpack\.(mix\.js|config\.js)|.*\.md)$ { + deny all; + access_log off; + log_not_found off; + } + + # Laravel-specific rules + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + + # Production optimizations + fastcgi_buffer_size 128k; + fastcgi_buffers 4 256k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + } + + # Error pages + error_page 404 /index.php; + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; +} \ No newline at end of file diff --git a/docker/nginx/conf.d/reverse-proxy.conf b/docker/nginx/conf.d/reverse-proxy.conf new file mode 100644 index 0000000..4c307a4 --- /dev/null +++ b/docker/nginx/conf.d/reverse-proxy.conf @@ -0,0 +1,137 @@ +# Reverse Proxy Configuration for Sibedas PBG Web +# This configuration handles SSL termination and routes traffic to the appropriate containers + +# Upstream for Sibedas application +upstream sibedas_backend { + server sibedas_app:9000; + keepalive 32; +} + +# Rate limiting +limit_req_zone $binary_remote_addr zone=sibedas_limit:10m rate=10r/s; + +# HTTP to HTTPS redirect +server { + listen 80; + server_name _; + + # Redirect all HTTP traffic to HTTPS + return 301 https://$host$request_uri; +} + +# HTTPS Server for Sibedas +server { + listen 443 ssl http2; + server_name sibedas.yourdomain.com; # Change this to your domain + + # SSL Configuration + ssl_certificate /etc/nginx/ssl/sibedas.crt; + ssl_certificate_key /etc/nginx/ssl/sibedas.key; + + # SSL Security Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Security Headers + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options DENY always; + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" always; + + # Rate limiting + limit_req zone=sibedas_limit burst=20 nodelay; + + # Client max body size + client_max_body_size 100M; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Root directory + root /var/www/public; + index index.php index.html index.htm; + + # Handle Laravel routes + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + # PHP-FPM configuration + location ~ \.php$ { + fastcgi_pass sibedas_backend; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + + # FastCGI optimizations + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + # Static files caching + location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Deny access to sensitive files + location ~ /\. { + deny all; + } + + location ~ /\.ht { + deny all; + } + + # Health check endpoint + location /health-check { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # Logging + access_log /var/log/nginx/sibedas_access.log; + error_log /var/log/nginx/sibedas_error.log; +} + +# Additional server blocks for other applications can be added here +# Example: +# server { +# listen 443 ssl http2; +# server_name other-app.yourdomain.com; +# +# ssl_certificate /etc/nginx/ssl/other-app.crt; +# ssl_certificate_key /etc/nginx/ssl/other-app.key; +# +# location / { +# proxy_pass http://other_app_container:port; +# proxy_set_header Host $host; +# proxy_set_header X-Real-IP $remote_addr; +# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# proxy_set_header X-Forwarded-Proto $scheme; +# } +# } \ No newline at end of file diff --git a/docker/nginx/conf.d/sibedas-internal.conf b/docker/nginx/conf.d/sibedas-internal.conf new file mode 100644 index 0000000..2ab63bc --- /dev/null +++ b/docker/nginx/conf.d/sibedas-internal.conf @@ -0,0 +1,58 @@ +# Internal Nginx Configuration for Sibedas PBG Web +# This configuration is for the internal container, accessed by reverse proxy + +server { + listen 80; + server_name localhost; + + # Root directory + root /var/www/public; + index index.php index.html index.htm; + + # Handle Laravel routes + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + # PHP-FPM configuration + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + + # FastCGI optimizations + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_connect_timeout 300; + fastcgi_send_timeout 300; + fastcgi_read_timeout 300; + } + + # Static files caching + location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Deny access to sensitive files + location ~ /\. { + deny all; + } + + location ~ /\.ht { + deny all; + } + + # Health check endpoint + location /health-check { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # Logging + access_log /var/log/nginx/sibedas_internal_access.log; + error_log /var/log/nginx/sibedas_internal_error.log; +} \ No newline at end of file diff --git a/docker/nginx/ssl-setup.sh b/docker/nginx/ssl-setup.sh new file mode 100644 index 0000000..344c35c --- /dev/null +++ b/docker/nginx/ssl-setup.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +# SSL Setup Script for Sibedas PBG Web +# This script handles SSL certificate generation and renewal + +set -e + +DOMAIN="${DOMAIN:-sibedas.yourdomain.com}" +EMAIL="${EMAIL:-admin@yourdomain.com}" +SSL_DIR="/etc/nginx/ssl" +CERT_FILE="$SSL_DIR/sibedas.crt" +KEY_FILE="$SSL_DIR/sibedas.key" + +# Function to generate self-signed certificate +generate_self_signed() { + echo "Generating self-signed SSL certificate for $DOMAIN..." + + # Create SSL directory if it doesn't exist + mkdir -p "$SSL_DIR" + + # Generate self-signed certificate + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$KEY_FILE" \ + -out "$CERT_FILE" \ + -subj "/C=ID/ST=Jakarta/L=Jakarta/O=Sibedas/OU=IT/CN=$DOMAIN/emailAddress=$EMAIL" + + echo "Self-signed certificate generated successfully!" +} + +# Function to setup Let's Encrypt certificate +setup_letsencrypt() { + echo "Setting up Let's Encrypt certificate for $DOMAIN..." + + # Check if certbot is available + if ! command -v certbot &> /dev/null; then + echo "Certbot not found. Installing..." + apk add --no-cache certbot certbot-nginx + fi + + # Stop nginx temporarily + nginx -s stop || true + + # Get certificate + certbot certonly --standalone \ + --email "$EMAIL" \ + --agree-tos \ + --no-eff-email \ + -d "$DOMAIN" + + # Copy certificates to nginx ssl directory + cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem "$CERT_FILE" + cp /etc/letsencrypt/live/$DOMAIN/privkey.pem "$KEY_FILE" + + # Set proper permissions + chmod 644 "$CERT_FILE" + chmod 600 "$KEY_FILE" + + # Start nginx + nginx + + echo "Let's Encrypt certificate setup completed!" +} + +# Function to renew Let's Encrypt certificate +renew_certificate() { + echo "Renewing Let's Encrypt certificate..." + + certbot renew --quiet + + # Copy renewed certificates + cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem "$CERT_FILE" + cp /etc/letsencrypt/live/$DOMAIN/privkey.pem "$KEY_FILE" + + # Reload nginx + nginx -s reload + + echo "Certificate renewal completed!" +} + +# Function to check certificate validity +check_certificate() { + if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then + echo "Certificate files exist." + echo "Certificate details:" + openssl x509 -in "$CERT_FILE" -text -noout | grep -E "(Subject:|Not Before|Not After)" + return 0 + else + echo "Certificate files not found." + return 1 + fi +} + +# Main script logic +case "${1:-setup}" in + "setup") + if [ "$SSL_TYPE" = "letsencrypt" ]; then + setup_letsencrypt + else + generate_self_signed + fi + ;; + "renew") + renew_certificate + ;; + "check") + check_certificate + ;; + "self-signed") + generate_self_signed + ;; + "letsencrypt") + setup_letsencrypt + ;; + *) + echo "Usage: $0 {setup|renew|check|self-signed|letsencrypt}" + echo "" + echo "Environment variables:" + echo " DOMAIN: Domain name (default: sibedas.yourdomain.com)" + echo " EMAIL: Email address for Let's Encrypt (default: admin@yourdomain.com)" + echo " SSL_TYPE: Type of SSL (letsencrypt or self-signed, default: self-signed)" + exit 1 + ;; +esac \ No newline at end of file diff --git a/docker/nginx/ssl/.gitignore b/docker/nginx/ssl/.gitignore new file mode 100644 index 0000000..7a47ea1 --- /dev/null +++ b/docker/nginx/ssl/.gitignore @@ -0,0 +1,14 @@ +# SSL Certificates - Do not commit these files +*.crt +*.key +*.pem +*.p12 +*.pfx + +# Let's Encrypt +letsencrypt/ +live/ +archive/ + +# Keep this directory in git but ignore certificate files +!.gitignore \ No newline at end of file diff --git a/docker/supervisor/laravel-production.conf b/docker/supervisor/laravel-production.conf index b290c89..b52df8e 100644 --- a/docker/supervisor/laravel-production.conf +++ b/docker/supervisor/laravel-production.conf @@ -6,13 +6,13 @@ pidfile=/var/run/supervisord.pid [program:laravel-queue-worker] process_name=%(program_name)s_%(process_num)02d -command=php /var/www/pupr/artisan queue:work --queue=default --timeout=82800 --tries=3 --memory=512 --sleep=3 -directory=/var/www/pupr +command=php /var/www/artisan queue:work --queue=default --timeout=82800 --tries=3 --memory=512 --sleep=3 +directory=/var/www autostart=true autorestart=true numprocs=2 redirect_stderr=true -stdout_logfile=/var/www/pupr/storage/logs/queue-worker.log +stdout_logfile=/var/www/storage/logs/queue-worker.log stdout_logfile_maxbytes=10MB stdout_logfile_backups=5 stopasgroup=true @@ -22,13 +22,13 @@ priority=10 [program:laravel-scheduler] process_name=%(program_name)s_%(process_num)02d -command=php /var/www/pupr/artisan schedule:work -directory=/var/www/pupr +command=php /var/www/artisan schedule:work +directory=/var/www autostart=true autorestart=true numprocs=1 redirect_stderr=true -stdout_logfile=/var/www/pupr/storage/logs/scheduler.log +stdout_logfile=/var/www/storage/logs/scheduler.log stdout_logfile_maxbytes=10MB stdout_logfile_backups=5 stopasgroup=true diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..4adbdb0 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,449 @@ +# Sibedas PBG Web - Documentation + +Dokumentasi lengkap untuk setup dan penggunaan aplikasi Sibedas PBG Web. + +## 📋 Table of Contents + +1. [Quick Start](#-quick-start) +2. [Architecture](#-architecture) +3. [Environment Setup](#-environment-setup) +4. [Production Deployment](#-production-deployment) +5. [SSL Configuration](#-ssl-configuration) +6. [Monitoring](#-monitoring) +7. [Troubleshooting](#-troubleshooting) + +## 🚀 Quick Start + +### Prerequisites + +- Docker & Docker Compose +- Domain name (untuk production) +- Port 80 dan 443 terbuka (untuk Let's Encrypt) + +### Local Development + +```bash +# Clone repository +git clone +cd sibedas-pbg-web + +# Setup local environment +./scripts/setup-local.sh +``` + +### Production Deployment + +```bash +# Copy environment file +cp env.production.example .env + +# Edit environment variables +nano .env + +# Deploy dengan reverse proxy dan SSL +./scripts/setup-reverse-proxy.sh setup +``` + +## 🏗️ Architecture + +### Local Development + +``` +Browser → Port 8000 → Nginx → PHP-FPM → MariaDB +``` + +### Production dengan Reverse Proxy + +``` +Internet → Reverse Proxy (80/443) → Internal Nginx → PHP-FPM → MariaDB +``` + +### Components + +- **Reverse Proxy Nginx**: Entry point, SSL termination, routing +- **Internal Nginx**: Serves Sibedas application +- **Application Container**: PHP-FPM with Supervisor (queue & scheduler) +- **Database Container**: MariaDB with backup import + +## ⚙️ Environment Setup + +### Required Variables + +```bash +# Domain & SSL +DOMAIN=sibedas.yourdomain.com +EMAIL=admin@yourdomain.com +SSL_TYPE=self-signed # atau letsencrypt + +# Database +DB_PASSWORD=your_secure_password +MYSQL_ROOT_PASSWORD=your_root_password + +# Laravel +APP_KEY=base64:your_app_key_here +APP_URL=https://sibedas.yourdomain.com +``` + +### Generate App Key + +```bash +docker-compose exec app php artisan key:generate +``` + +## 🚀 Production Deployment + +### Step-by-Step Production Deployment + +#### 1. **Server Preparation** + +```bash +# Update system +sudo apt update && sudo apt upgrade -y + +# Install Docker & Docker Compose +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +sudo usermod -aG docker $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 + +# Logout and login again for group changes +exit +# SSH back to server +``` + +#### 2. **Clone Repository** + +```bash +# Clone project +git clone +cd sibedas-pbg-web + +# Set proper permissions +chmod +x scripts/*.sh +``` + +#### 3. **Environment Configuration** + +```bash +# Copy environment template +cp env.production.example .env + +# Edit environment variables +nano .env +``` + +**Required Environment Variables:** + +```bash +# Domain & SSL +DOMAIN=sibedas.yourdomain.com +EMAIL=admin@yourdomain.com +SSL_TYPE=letsencrypt # atau self-signed untuk testing + +# Database +DB_DATABASE=sibedas +DB_USERNAME=sibedas_user +DB_PASSWORD=your_secure_database_password +MYSQL_ROOT_PASSWORD=your_secure_root_password + +# Laravel +APP_NAME="Sibedas PBG Web" +APP_ENV=production +APP_DEBUG=false +APP_KEY=base64:your_app_key_here +APP_URL=https://sibedas.yourdomain.com +VITE_APP_URL=https://sibedas.yourdomain.com + +# Mail Configuration +MAIL_MAILER=smtp +MAIL_HOST=smtp.gmail.com +MAIL_PORT=587 +MAIL_USERNAME=your_email@gmail.com +MAIL_PASSWORD=your_app_password +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS=your_email@gmail.com +MAIL_FROM_NAME="Sibedas PBG Web" + +# Google Sheets API +SPREAD_SHEET_ID=your_google_sheets_id_here +``` + +#### 4. **Generate Application Key** + +```bash +# Generate Laravel app key +php artisan key:generate --show +# Copy the generated key to .env file +``` + +#### 5. **Deploy Application** + +```bash +# Option A: Full deployment with SSL (Recommended) +./scripts/setup-reverse-proxy.sh setup + +# Option B: Deploy without SSL first +./scripts/deploy-production.sh deploy +``` + +#### 6. **SSL Setup (if not done in step 5)** + +```bash +# For Let's Encrypt (Production) +DOMAIN=yourdomain.com EMAIL=admin@yourdomain.com ./scripts/setup-ssl.sh letsencrypt + +# For Self-Signed (Testing) +./scripts/setup-ssl.sh self-signed +``` + +#### 7. **Verify Deployment** + +```bash +# Check container status +docker-compose ps + +# Check application health +curl -f http://localhost/health-check + +# Check SSL certificate +./scripts/setup-ssl.sh check + +# View logs +docker-compose logs nginx-proxy +docker-compose logs app +``` + +### Scripts yang Diperlukan + +#### **Essential Scripts (Wajib)** + +- `scripts/setup-reverse-proxy.sh` - Setup lengkap reverse proxy dan SSL +- `scripts/deploy-production.sh` - Deployment production +- `scripts/setup-ssl.sh` - Setup SSL certificates + +#### **Optional Scripts** + +- `scripts/setup-local.sh` - Setup local development +- `scripts/import-sibedas-database.sh` - Manual database import (otomatis via docker-compose) + +#### **Scripts yang Tidak Diperlukan** + +- `scripts/build-and-zip.sh` - Tidak diperlukan karena menggunakan Docker build + +### Deployment Commands Summary + +```bash +# 1. Setup environment +cp env.production.example .env +nano .env + +# 2. Deploy with SSL (Recommended) +./scripts/setup-reverse-proxy.sh setup + +# 3. Or deploy step by step +./scripts/deploy-production.sh deploy +./scripts/setup-ssl.sh letsencrypt + +# 4. Check status +./scripts/setup-reverse-proxy.sh status +``` + +## 🔒 SSL Configuration + +### Self-Signed Certificate + +```bash +SSL_TYPE=self-signed ./scripts/setup-reverse-proxy.sh setup +``` + +### Let's Encrypt Certificate + +```bash +DOMAIN=myapp.com EMAIL=admin@myapp.com SSL_TYPE=letsencrypt ./scripts/setup-reverse-proxy.sh setup +``` + +### SSL Management + +```bash +# Check certificate +./scripts/setup-ssl.sh check + +# Renew certificate +./scripts/setup-ssl.sh renew +``` + +## 📊 Monitoring + +### Container Status + +```bash +# Check all containers +docker-compose ps + +# Check specific service +docker-compose ps app +``` + +### Logs + +```bash +# Application logs +docker-compose logs app + +# Reverse proxy logs +docker-compose logs nginx-proxy + +# Database logs +docker-compose logs db + +# Follow logs +docker-compose logs -f nginx-proxy +``` + +### Health Checks + +```bash +# Application health +curl -f http://localhost/health-check + +# SSL certificate +./scripts/setup-ssl.sh check +``` + +## 🛠️ Troubleshooting + +### SSL Issues + +```bash +# Check certificate files +docker exec sibedas_nginx_proxy ls -la /etc/nginx/ssl/ + +# Test SSL connection +openssl s_client -connect yourdomain.com:443 + +# Check nginx config +docker exec sibedas_nginx_proxy nginx -t +``` + +### Container Issues + +```bash +# Restart services +docker-compose restart + +# Check network +docker network ls + +# Check volumes +docker volume ls +``` + +### Database Issues + +```bash +# Import database manually +./scripts/import-sibedas-database.sh + +# Check database connection +docker exec sibedas_app php artisan db:monitor +``` + +### Performance Issues + +```bash +# Check resource usage +docker stats + +# Check nginx access logs +docker exec sibedas_nginx_proxy tail -f /var/log/nginx/sibedas_access.log +``` + +## 🔧 Maintenance + +### Backup + +```bash +# Database backup +docker exec sibedas_db mysqldump -u root -p sibedas > backup.sql + +# Volume backup +docker run --rm -v sibedas_app_storage:/data -v $(pwd):/backup alpine tar czf /backup/storage.tar.gz -C /data . +``` + +### Update Application + +```bash +# Pull latest changes +git pull + +# Rebuild and restart +docker-compose up -d --build +``` + +### SSL Certificate Renewal + +```bash +# Manual renewal +./scripts/setup-ssl.sh renew + +# Automatic renewal (cron) +0 12 * * * /path/to/sibedas-pbg-web/scripts/setup-ssl.sh renew +``` + +## 📁 Project Structure + +``` +sibedas-pbg-web/ +├── docker/ # Docker configurations +│ ├── nginx/ # Nginx configs +│ ├── mysql/ # MySQL configs +│ └── supervisor/ # Supervisor configs +├── scripts/ # Deployment scripts +│ ├── setup-local.sh # Local development +│ ├── setup-reverse-proxy.sh # Reverse proxy setup +│ ├── deploy-production.sh # Production deployment +│ ├── setup-ssl.sh # SSL setup +│ └── import-sibedas-database.sh # Database import +├── docs/ # Documentation +├── docker-compose.yml # Production compose +├── docker-compose.local.yml # Local development compose +└── README.md # Main README +``` + +## 🆘 Support + +### Common Commands + +```bash +# Start services +docker-compose up -d + +# Stop services +docker-compose down + +# View logs +docker-compose logs [service] + +# Execute commands +docker-compose exec app php artisan [command] + +# Check status +./scripts/setup-reverse-proxy.sh status +``` + +### Getting Help + +1. Check logs: `docker-compose logs [service]` +2. Check status: `./scripts/setup-reverse-proxy.sh status` +3. Restart services: `docker-compose restart` +4. Review this documentation + +## 📚 Additional Resources + +- [Docker Documentation](https://docs.docker.com/) +- [Nginx Documentation](https://nginx.org/en/docs/) +- [Let's Encrypt Documentation](https://letsencrypt.org/docs/) +- [Laravel Documentation](https://laravel.com/docs/) diff --git a/env.production.example b/env.production.example new file mode 100644 index 0000000..145ead9 --- /dev/null +++ b/env.production.example @@ -0,0 +1,154 @@ +# Production Environment Configuration for Sibedas PBG Web +# Copy this file to .env and update the values + +# ============================================================================= +# REVERSE PROXY & SSL CONFIGURATION +# ============================================================================= + +# Domain configuration +DOMAIN=sibedas.yourdomain.com +EMAIL=admin@yourdomain.com +SSL_TYPE=self-signed # Options: self-signed, letsencrypt + +# Nginx ports (usually 80 and 443) +NGINX_HTTP_PORT=80 +NGINX_HTTPS_PORT=443 + +# ============================================================================= +# LARAVEL APPLICATION CONFIGURATION +# ============================================================================= + +# Application settings +APP_NAME="Sibedas PBG Web" +APP_ENV=production +APP_DEBUG=false +APP_KEY=base64:your_app_key_here +APP_URL=https://sibedas.yourdomain.com +APP_TIMEZONE=Asia/Jakarta + +# Vite configuration +VITE_APP_URL=https://sibedas.yourdomain.com + +# ============================================================================= +# DATABASE CONFIGURATION +# ============================================================================= + +# Database connection +DB_CONNECTION=mariadb +DB_HOST=db +DB_PORT=3306 +DB_DATABASE=sibedas +DB_USERNAME=sibedas_user +DB_PASSWORD=your_secure_database_password + +# MySQL root password +MYSQL_ROOT_PASSWORD=your_secure_root_password + +# MySQL performance tuning +MYSQL_INNODB_BUFFER_POOL_SIZE=1G + +# External database port (optional, for external access) +DB_EXTERNAL_PORT=3306 + +# ============================================================================= +# CACHE, SESSION & QUEUE CONFIGURATION +# ============================================================================= + +# Cache configuration (using database) +CACHE_DRIVER=database + +# Session configuration (using database) +SESSION_DRIVER=database +SESSION_LIFETIME=120 + +# Queue configuration (using database) +QUEUE_CONNECTION=database + +# ============================================================================= +# MAIL CONFIGURATION +# ============================================================================= + +# Mail settings +MAIL_MAILER=smtp +MAIL_HOST=smtp.gmail.com +MAIL_PORT=587 +MAIL_USERNAME=your_email@gmail.com +MAIL_PASSWORD=your_app_password +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS=your_email@gmail.com +MAIL_FROM_NAME="Sibedas PBG Web" + +# ============================================================================= +# GOOGLE SHEETS API CONFIGURATION +# ============================================================================= + +# Google Sheets API +SPREAD_SHEET_ID=your_google_sheets_id_here + +# ============================================================================= +# LOGGING CONFIGURATION +# ============================================================================= + +# Log settings +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=warning + +# ============================================================================= +# SECURITY CONFIGURATION +# ============================================================================= + +# Security settings +SESSION_SECURE_COOKIE=true +SESSION_SAME_SITE=lax + +# ============================================================================= +# PERFORMANCE CONFIGURATION +# ============================================================================= + +# Performance settings +CACHE_TTL=3600 +SESSION_SECURE_COOKIE=true + +# ============================================================================= +# BACKUP CONFIGURATION +# ============================================================================= + +# Backup settings +BACKUP_RETENTION_DAYS=30 +BACKUP_PATH=./backups + +# ============================================================================= +# MONITORING CONFIGURATION +# ============================================================================= + +# Health check settings +HEALTH_CHECK_ENABLED=true +HEALTH_CHECK_INTERVAL=30s + +# ============================================================================= +# NOTES +# ============================================================================= + +# 1. Generate APP_KEY: php artisan key:generate +# 2. Update DOMAIN and EMAIL for your domain +# 3. Set secure passwords for database +# 4. Configure mail settings for notifications +# 5. Set Google Sheets API credentials +# 6. For Let's Encrypt: ensure domain points to server and ports 80/443 are open + +# ============================================================================= +# DEPLOYMENT COMMANDS +# ============================================================================= + +# Setup reverse proxy and SSL: +# ./setup-reverse-proxy.sh setup + +# Setup SSL only: +# ./setup-reverse-proxy.sh ssl + +# Check status: +# ./setup-reverse-proxy.sh status + +# Deploy production: +# ./scripts/deploy-production.sh deploy \ No newline at end of file diff --git a/build-and-zip.sh b/scripts/build-and-zip.sh similarity index 100% rename from build-and-zip.sh rename to scripts/build-and-zip.sh diff --git a/scripts/deploy-production.sh b/scripts/deploy-production.sh new file mode 100755 index 0000000..6487bdc --- /dev/null +++ b/scripts/deploy-production.sh @@ -0,0 +1,226 @@ +#!/bin/bash + +# Production Deployment Script for Sibedas PBG Web +# This script deploys the application with reverse proxy and SSL support + +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 +DOMAIN="${DOMAIN:-sibedas.yourdomain.com}" +EMAIL="${EMAIL:-admin@yourdomain.com}" +SSL_TYPE="${SSL_TYPE:-self-signed}" + +echo -e "${BLUE}=== Production Deployment for Sibedas PBG Web ===${NC}" +echo -e "Domain: ${GREEN}$DOMAIN${NC}" +echo -e "Email: ${GREEN}$EMAIL${NC}" +echo -e "SSL Type: ${GREEN}$SSL_TYPE${NC}" +echo "" + +# Function to check prerequisites +check_prerequisites() { + echo -e "${BLUE}Checking prerequisites...${NC}" + + # Check if Docker is installed + if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed${NC}" + exit 1 + fi + + # Check if Docker Compose is installed + if ! command -v docker-compose &> /dev/null; then + echo -e "${RED}Error: Docker Compose is not installed${NC}" + exit 1 + fi + + # Check if .env file exists + if [ ! -f .env ]; then + echo -e "${RED}Error: .env file not found${NC}" + echo -e "${YELLOW}Please create .env file with required environment variables${NC}" + exit 1 + fi + + echo -e "${GREEN}Prerequisites check passed!${NC}" +} + +# Function to backup existing data +backup_data() { + echo -e "${BLUE}Creating backup of existing data...${NC}" + + BACKUP_DIR="backups/$(date +%Y%m%d_%H%M%S)" + mkdir -p "$BACKUP_DIR" + + # Backup database + if docker ps | grep -q sibedas_db; then + echo -e "${YELLOW}Backing up database...${NC}" + docker exec sibedas_db mysqldump -u root -p"${MYSQL_ROOT_PASSWORD:-root}" sibedas > "$BACKUP_DIR/database.sql" || true + fi + + # Backup volumes + echo -e "${YELLOW}Backing up volumes...${NC}" + docker run --rm -v sibedas_app_storage:/data -v "$(pwd)/$BACKUP_DIR":/backup alpine tar czf /backup/app_storage.tar.gz -C /data . || true + docker run --rm -v sibedas_dbdata:/data -v "$(pwd)/$BACKUP_DIR":/backup alpine tar czf /backup/dbdata.tar.gz -C /data . || true + + echo -e "${GREEN}Backup created in $BACKUP_DIR${NC}" +} + +# Function to stop existing containers +stop_containers() { + echo -e "${BLUE}Stopping existing containers...${NC}" + + docker-compose down --remove-orphans || true + + echo -e "${GREEN}Containers stopped!${NC}" +} + +# Function to build and start containers +deploy_containers() { + echo -e "${BLUE}Building and starting containers...${NC}" + + # Build images + echo -e "${YELLOW}Building Docker images...${NC}" + docker-compose build --no-cache + + # Start containers + echo -e "${YELLOW}Starting containers...${NC}" + docker-compose up -d + + # Wait for containers to be healthy + echo -e "${YELLOW}Waiting for containers to be healthy...${NC}" + sleep 30 + + # Check container status + if ! docker-compose ps | grep -q "Up"; then + echo -e "${RED}Error: Some containers failed to start${NC}" + docker-compose logs + exit 1 + fi + + echo -e "${GREEN}Containers deployed successfully!${NC}" +} + +# Function to setup SSL +setup_ssl() { + echo -e "${BLUE}Setting up SSL certificate...${NC}" + + # Wait for nginx proxy to be ready + echo -e "${YELLOW}Waiting for reverse proxy to be ready...${NC}" + sleep 10 + + # Setup SSL + if [ "$SSL_TYPE" = "letsencrypt" ]; then + echo -e "${YELLOW}Setting up Let's Encrypt certificate...${NC}" + echo -e "${YELLOW}Make sure your domain $DOMAIN points to this server${NC}" + read -p "Press Enter to continue..." + + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh letsencrypt + else + echo -e "${YELLOW}Setting up self-signed certificate...${NC}" + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh self-signed + fi + + echo -e "${GREEN}SSL setup completed!${NC}" +} + +# Function to run post-deployment tasks +post_deployment() { + echo -e "${BLUE}Running post-deployment tasks...${NC}" + + # Clear Laravel caches + echo -e "${YELLOW}Clearing Laravel caches...${NC}" + docker exec sibedas_app php artisan config:clear || true + docker exec sibedas_app php artisan route:clear || true + docker exec sibedas_app php artisan view:clear || true + docker exec sibedas_app php artisan cache:clear || true + + # Optimize Laravel + echo -e "${YELLOW}Optimizing Laravel...${NC}" + docker exec sibedas_app php artisan optimize || true + + # Check application health + echo -e "${YELLOW}Checking application health...${NC}" + sleep 5 + + if curl -f -s "http://localhost/health-check" > /dev/null; then + echo -e "${GREEN}Application is healthy!${NC}" + else + echo -e "${YELLOW}Warning: Health check failed, but deployment completed${NC}" + fi +} + +# Function to show deployment status +show_status() { + echo -e "${BLUE}=== Deployment Status ===${NC}" + + echo -e "${YELLOW}Container Status:${NC}" + docker-compose ps + + echo -e "${YELLOW}SSL Certificate Status:${NC}" + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh check || true + + echo -e "${YELLOW}Application URLs:${NC}" + echo -e " HTTP: ${GREEN}http://$DOMAIN${NC}" + echo -e " HTTPS: ${GREEN}https://$DOMAIN${NC}" + + echo -e "${YELLOW}Logs:${NC}" + echo -e " Application: ${GREEN}docker-compose logs app${NC}" + echo -e " Reverse Proxy: ${GREEN}docker-compose logs nginx-proxy${NC}" + echo -e " Database: ${GREEN}docker-compose logs db${NC}" +} + +# Function to show usage +show_usage() { + echo "Usage: $0 {deploy|status|backup|ssl}" + echo "" + echo "Commands:" + echo " deploy - Full deployment with SSL setup" + echo " status - Show deployment status" + echo " backup - Create backup of existing data" + echo " ssl - Setup SSL certificate only" + echo "" + echo "Environment variables:" + echo " DOMAIN - Domain name (default: sibedas.yourdomain.com)" + echo " EMAIL - Email address for Let's Encrypt (default: admin@yourdomain.com)" + echo " SSL_TYPE - Type of SSL (letsencrypt or self-signed, default: self-signed)" + echo "" + echo "Examples:" + echo " DOMAIN=myapp.com EMAIL=admin@myapp.com SSL_TYPE=letsencrypt $0 deploy" + echo " $0 status" + echo " $0 ssl" +} + +# Main script logic +case "${1:-deploy}" in + "deploy") + check_prerequisites + backup_data + stop_containers + deploy_containers + setup_ssl + post_deployment + show_status + ;; + "status") + show_status + ;; + "backup") + backup_data + ;; + "ssl") + setup_ssl + ;; + *) + show_usage + exit 1 + ;; +esac + +echo "" +echo -e "${GREEN}Deployment completed successfully!${NC}" +echo -e "${BLUE}Your application is now accessible at: https://$DOMAIN${NC}" \ No newline at end of file diff --git a/scripts/import-sibedas-database.sh b/scripts/import-sibedas-database.sh new file mode 100755 index 0000000..f77fc5f --- /dev/null +++ b/scripts/import-sibedas-database.sh @@ -0,0 +1,257 @@ +#!/bin/bash + +echo "🗃️ Import Database from sibedas.sql" +echo "====================================" + +# 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}[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" +} + +# Check if sibedas.sql exists +check_sql_file() { + if [[ ! -f "../sibedas.sql" ]]; then + print_error "sibedas.sql file not found!" + print_error "Please make sure sibedas.sql is in the project root directory." + exit 1 + fi + + print_success "Found sibedas.sql file" +} + +# Import for local development +import_local() { + print_status "Importing database for LOCAL DEVELOPMENT..." + + # Stop containers if running + print_status "Stopping containers..." + docker-compose -f ../docker-compose.local.yml down + + # Remove existing database volume to force fresh import + print_warning "Removing old database volume for fresh import..." + docker volume rm sibedas-pbg-web_sibedas_dbdata_local 2>/dev/null || true + + # Start database container first + print_status "Starting database container..." + docker-compose -f ../docker-compose.local.yml up -d db + + # Wait for database to be ready + print_status "Waiting for database to be ready..." + sleep 20 + + # Verify sibedas.sql was imported automatically + print_status "Verifying database import..." + if docker-compose -f ../docker-compose.local.yml exec -T db mysql -uroot -proot -e "USE sibedas; SELECT COUNT(*) FROM users;" 2>/dev/null; then + print_success "✅ Database imported successfully from sibedas.sql!" + + # Show table summary + print_status "Database tables summary:" + docker-compose -f ../docker-compose.local.yml exec -T db mysql -uroot -proot -e " + USE sibedas; + SELECT 'users' as table_name, COUNT(*) as count FROM users + UNION SELECT 'advertisements', COUNT(*) FROM advertisements + UNION SELECT 'business_or_industries', COUNT(*) FROM business_or_industries + UNION SELECT 'customers', COUNT(*) FROM customers + UNION SELECT 'cache', COUNT(*) FROM cache + UNION SELECT 'sessions', COUNT(*) FROM sessions + UNION SELECT 'jobs', COUNT(*) FROM jobs;" + + else + print_error "❌ Database import failed or data not found!" + exit 1 + fi + + # Start all containers + print_status "Starting all containers..." + docker-compose -f ../docker-compose.local.yml up -d + + # Wait for app to be ready + sleep 15 + + # Clear caches to ensure fresh start + print_status "Clearing application caches..." + docker-compose -f ../docker-compose.local.yml exec -T app php artisan config:clear + docker-compose -f ../docker-compose.local.yml exec -T app php artisan cache:clear + docker-compose -f ../docker-compose.local.yml exec -T app php artisan view:clear + + print_success "✅ Local development setup completed with sibedas.sql data!" + print_status "Access your application at: http://localhost:8000" +} + +# Import for production +import_production() { + print_status "Importing database for PRODUCTION..." + + # Check if .env exists + if [[ ! -f "../.env" ]]; then + print_error ".env file not found! Please configure production environment first." + exit 1 + fi + + # Load environment variables + source ../.env + + # Stop containers if running + print_status "Stopping containers..." + docker-compose -f ../docker-compose.yml down + + # Remove existing database volume to force fresh import + print_warning "Removing old database volume for fresh import..." + docker volume rm sibedas-pbg-web_sibedas_dbdata 2>/dev/null || true + + # Start database container first + print_status "Starting database container..." + docker-compose -f ../docker-compose.yml up -d db + + # Wait for database to be ready + print_status "Waiting for database to be ready..." + sleep 30 + + # Verify sibedas.sql was imported automatically + print_status "Verifying database import..." + if docker-compose -f ../docker-compose.yml exec -T db mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "USE ${DB_DATABASE}; SELECT COUNT(*) FROM users;" 2>/dev/null; then + print_success "✅ Database imported successfully from sibedas.sql!" + + # Show table summary + print_status "Database tables summary:" + docker-compose -f ../docker-compose.yml exec -T db mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e " + USE ${DB_DATABASE}; + SELECT 'users' as table_name, COUNT(*) as count FROM users + UNION SELECT 'advertisements', COUNT(*) FROM advertisements + UNION SELECT 'business_or_industries', COUNT(*) FROM business_or_industries + UNION SELECT 'customers', COUNT(*) FROM customers + UNION SELECT 'cache', COUNT(*) FROM cache + UNION SELECT 'sessions', COUNT(*) FROM sessions + UNION SELECT 'jobs', COUNT(*) FROM jobs;" + + else + print_error "❌ Database import failed or data not found!" + exit 1 + fi + + # Start all containers + print_status "Starting all containers..." + docker-compose -f ../docker-compose.yml up -d + + # Wait for app to be ready + sleep 30 + + # Generate app key if needed + if [[ -z "$APP_KEY" ]] || [[ "$APP_KEY" == "" ]]; then + print_status "Generating application key..." + docker-compose -f ../docker-compose.yml exec -T app php artisan key:generate --force + fi + + # Optimize application + print_status "Optimizing application..." + docker-compose -f ../docker-compose.yml exec -T app php artisan config:cache + docker-compose -f ../docker-compose.yml exec -T app php artisan route:cache + docker-compose -f ../docker-compose.yml exec -T app php artisan view:cache + + # Create storage link + print_status "Creating storage link..." + docker-compose -f ../docker-compose.yml exec -T app php artisan storage:link + + print_success "✅ Production setup completed with sibedas.sql data!" + print_status "Access your application at: ${APP_URL}" +} + +# Manual import to running container +manual_import() { + print_status "Manual import to running container..." + + # Check which containers are running + if docker-compose -f ../docker-compose.local.yml ps | grep -q "sibedas_db_local"; then + print_status "Found local development database container" + print_status "Importing sibedas.sql..." + docker-compose -f ../docker-compose.local.yml exec -T db mysql -uroot -proot sibedas < ../sibedas.sql + print_success "✅ Import completed for local development!" + + # Clear app caches + docker-compose -f ../docker-compose.local.yml exec -T app php artisan cache:clear 2>/dev/null || true + + elif docker-compose -f ../docker-compose.yml ps | grep -q "sibedas_db"; then + print_status "Found production database container" + source ../.env 2>/dev/null || true + print_status "Importing sibedas.sql..." + docker-compose -f ../docker-compose.yml exec -T db mysql -uroot -p${MYSQL_ROOT_PASSWORD:-root} ${DB_DATABASE:-sibedas} < ../sibedas.sql + print_success "✅ Import completed for production!" + + # Clear app caches + docker-compose -f ../docker-compose.yml exec -T app php artisan cache:clear 2>/dev/null || true + + else + print_error "❌ No database container found running!" + print_error "Please start containers first:" + print_error " Local: docker-compose -f ../docker-compose.local.yml up -d" + print_error " Production: docker-compose -f ../docker-compose.yml up -d" + exit 1 + fi +} + +# Main execution +main() { + check_sql_file + + echo "" + echo "🗃️ Choose import method:" + echo "1) 🔄 Fresh import for LOCAL development (recommended)" + echo "2) 🔄 Fresh import for PRODUCTION" + echo "3) 📥 Manual import to running container" + echo "4) ❌ Cancel" + echo "" + + read -p "Enter your choice (1-4): " choice + + case $choice in + 1) + import_local + ;; + 2) + import_production + ;; + 3) + manual_import + ;; + 4) + print_status "Cancelled" + exit 0 + ;; + *) + print_error "Invalid choice" + exit 1 + ;; + esac + + echo "" + print_success "🎉 Database import completed!" + echo "" + print_status "📋 Imported data includes:" + echo " ✅ All application tables with existing data" + echo " ✅ Cache table (for CACHE_DRIVER=database)" + echo " ✅ Sessions table (for SESSION_DRIVER=database)" + echo " ✅ Jobs & failed_jobs tables (for QUEUE_CONNECTION=database)" + echo "" + print_status "🚀 Your application is ready to use with all data!" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/scripts/setup-local.sh b/scripts/setup-local.sh new file mode 100755 index 0000000..3eef0ab --- /dev/null +++ b/scripts/setup-local.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# Local Development Setup Script for Sibedas PBG Web +# This script sets up the local development environment + +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 + +echo -e "${BLUE}=== Local Development Setup untuk Sibedas PBG Web ===${NC}" +echo "" + +# Function to check prerequisites +check_prerequisites() { + echo -e "${BLUE}Checking prerequisites...${NC}" + + # Check if Docker is installed + if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed${NC}" + exit 1 + fi + + # Check if Docker Compose is installed + if ! command -v docker-compose &> /dev/null; then + echo -e "${RED}Error: Docker Compose is not installed${NC}" + exit 1 + fi + + # Check if .env file exists + if [ ! -f .env ]; then + echo -e "${YELLOW}Warning: .env file not found${NC}" + echo -e "${YELLOW}Creating from example...${NC}" + if [ -f env.production.example ]; then + cp env.production.example .env + else + echo -e "${RED}Error: No environment example file found${NC}" + exit 1 + fi + fi + + echo -e "${GREEN}Prerequisites check passed!${NC}" +} + +# Function to setup environment for local development +setup_environment() { + echo -e "${BLUE}Setting up environment for local development...${NC}" + + # Update .env for local development + sed -i 's/APP_ENV=production/APP_ENV=local/g' .env + sed -i 's/APP_DEBUG=false/APP_DEBUG=true/g' .env + sed -i 's/APP_URL=https:\/\/sibedas.yourdomain.com/APP_URL=http:\/\/localhost:8000/g' .env + sed -i 's/VITE_APP_URL=https:\/\/sibedas.yourdomain.com/VITE_APP_URL=http:\/\/localhost:8000/g' .env + + # Update database settings for local + sed -i 's/DB_USERNAME=sibedas_user/DB_USERNAME=root/g' .env + sed -i 's/DB_PASSWORD=your_secure_database_password/DB_PASSWORD=root/g' .env + sed -i 's/MYSQL_ROOT_PASSWORD=your_secure_root_password/MYSQL_ROOT_PASSWORD=root/g' .env + + echo -e "${GREEN}Environment configured for local development!${NC}" +} + +# Function to start local containers +start_containers() { + echo -e "${BLUE}Starting local development containers...${NC}" + + # Stop any existing containers + docker-compose -f docker-compose.local.yml down --remove-orphans || true + + # Build and start containers + docker-compose -f docker-compose.local.yml up -d --build + + # Wait for containers to be ready + echo -e "${YELLOW}Waiting for containers to be ready...${NC}" + sleep 30 + + # Check container status + if docker-compose -f docker-compose.local.yml ps | grep -q "Up"; then + echo -e "${GREEN}Containers started successfully!${NC}" + else + echo -e "${RED}Error: Some containers failed to start${NC}" + docker-compose -f docker-compose.local.yml logs + exit 1 + fi +} + +# Function to setup database +setup_database() { + echo -e "${BLUE}Setting up database...${NC}" + + # Wait for database to be ready + echo -e "${YELLOW}Waiting for database to be ready...${NC}" + sleep 10 + + # Check if database import was successful + if docker exec sibedas_db_local mysql -uroot -proot sibedas -e "SHOW TABLES LIKE 'users';" 2>/dev/null | grep -q "users"; then + echo -e "${GREEN}Database imported successfully from sibedas.sql!${NC}" + else + echo -e "${YELLOW}Warning: Database import verification failed${NC}" + echo -e "${YELLOW}You may need to manually import the database${NC}" + fi +} + +# Function to run post-setup tasks +post_setup() { + echo -e "${BLUE}Running post-setup tasks...${NC}" + + # Clear Laravel caches + echo -e "${YELLOW}Clearing Laravel caches...${NC}" + docker exec sibedas_app_local php artisan config:clear || true + docker exec sibedas_app_local php artisan route:clear || true + docker exec sibedas_app_local php artisan view:clear || true + docker exec sibedas_app_local php artisan cache:clear || true + + # Optimize Laravel + echo -e "${YELLOW}Optimizing Laravel...${NC}" + docker exec sibedas_app_local php artisan optimize:clear || true + + # Create storage link + echo -e "${YELLOW}Creating storage link...${NC}" + docker exec sibedas_app_local php artisan storage:link || true + + echo -e "${GREEN}Post-setup tasks completed!${NC}" +} + +# Function to show status +show_status() { + echo -e "${BLUE}=== Local Development Status ===${NC}" + + echo -e "${YELLOW}Container Status:${NC}" + docker-compose -f docker-compose.local.yml ps + + echo -e "${YELLOW}Application URLs:${NC}" + echo -e " Main App: ${GREEN}http://localhost:8000${NC}" + echo -e " Vite Dev: ${GREEN}http://localhost:5173${NC}" + + echo -e "${YELLOW}Useful Commands:${NC}" + echo -e " View logs: ${GREEN}docker-compose -f docker-compose.local.yml logs -f [service]${NC}" + echo -e " Execute commands: ${GREEN}docker-compose -f docker-compose.local.yml exec app php artisan [command]${NC}" + echo -e " Stop services: ${GREEN}docker-compose -f docker-compose.local.yml down${NC}" + echo -e " Restart services: ${GREEN}docker-compose -f docker-compose.local.yml restart${NC}" +} + +# Function to show usage +show_usage() { + echo "Usage: $0 {setup|status|help}" + echo "" + echo "Commands:" + echo " setup - Setup local development environment (default)" + echo " status - Show current status" + echo " help - Show this help message" + echo "" + echo "Examples:" + echo " $0 setup" + echo " $0 status" +} + +# Main script logic +case "${1:-setup}" in + "setup") + check_prerequisites + setup_environment + start_containers + setup_database + post_setup + show_status + ;; + "status") + show_status + ;; + "help"|"-h"|"--help") + show_usage + ;; + *) + echo -e "${RED}Unknown command: $1${NC}" + echo "" + show_usage + exit 1 + ;; +esac + +echo "" +echo -e "${GREEN}Local development setup completed successfully!${NC}" +echo -e "${BLUE}You can now access your application at: http://localhost:8000${NC}" \ No newline at end of file diff --git a/scripts/setup-reverse-proxy.sh b/scripts/setup-reverse-proxy.sh new file mode 100755 index 0000000..e4abf62 --- /dev/null +++ b/scripts/setup-reverse-proxy.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# Reverse Proxy Setup Script for Sibedas PBG Web +# Wrapper script untuk setup reverse proxy dan SSL + +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 + +echo -e "${BLUE}=== Reverse Proxy Setup untuk Sibedas PBG Web ===${NC}" +echo "" + +# Function to show usage +show_usage() { + echo "Usage: $0 {setup|ssl|status|help}" + echo "" + echo "Commands:" + echo " setup - Setup reverse proxy dan SSL (default)" + echo " ssl - Setup SSL certificate only" + echo " status - Show current status" + echo " help - Show this help message" + echo "" + echo "Environment variables:" + echo " DOMAIN - Domain name (default: sibedas.yourdomain.com)" + echo " EMAIL - Email address for Let's Encrypt (default: admin@yourdomain.com)" + echo " SSL_TYPE - Type of SSL (letsencrypt or self-signed, default: self-signed)" + echo "" + echo "Examples:" + echo " $0 setup" + echo " DOMAIN=myapp.com EMAIL=admin@myapp.com SSL_TYPE=letsencrypt $0 setup" + echo " $0 ssl" + echo " $0 status" +} + +# Function to check prerequisites +check_prerequisites() { + echo -e "${BLUE}Checking prerequisites...${NC}" + + # Check if .env exists + if [ ! -f .env ]; then + echo -e "${RED}Error: .env file not found${NC}" + echo -e "${YELLOW}Please create .env file with required environment variables${NC}" + exit 1 + fi + + # Check if scripts directory exists + if [ ! -d scripts ]; then + echo -e "${RED}Error: scripts directory not found${NC}" + exit 1 + fi + + echo -e "${GREEN}Prerequisites check passed!${NC}" +} + +# Function to setup reverse proxy +setup_reverse_proxy() { + echo -e "${BLUE}Setting up reverse proxy...${NC}" + + # Run deployment script + ./scripts/deploy-production.sh deploy + + echo -e "${GREEN}Reverse proxy setup completed!${NC}" +} + +# Function to setup SSL only +setup_ssl() { + echo -e "${BLUE}Setting up SSL certificate...${NC}" + + # Run SSL setup script + ./scripts/setup-ssl.sh setup + + echo -e "${GREEN}SSL setup completed!${NC}" +} + +# Function to show status +show_status() { + echo -e "${BLUE}=== Current Status ===${NC}" + + # Check if containers are running + if command -v docker-compose &> /dev/null; then + echo -e "${YELLOW}Container Status:${NC}" + docker-compose ps 2>/dev/null || echo "Docker Compose not available" + fi + + # Check SSL certificate + if [ -f scripts/setup-ssl.sh ]; then + echo -e "${YELLOW}SSL Certificate Status:${NC}" + ./scripts/setup-ssl.sh check 2>/dev/null || echo "SSL check failed" + fi + + # Show environment info + if [ -f .env ]; then + echo -e "${YELLOW}Environment Variables:${NC}" + grep -E "^(DOMAIN|EMAIL|SSL_TYPE|APP_URL)=" .env 2>/dev/null || echo "No environment variables found" + fi +} + +# Main script logic +case "${1:-setup}" in + "setup") + check_prerequisites + setup_reverse_proxy + ;; + "ssl") + check_prerequisites + setup_ssl + ;; + "status") + show_status + ;; + "help"|"-h"|"--help") + show_usage + ;; + *) + echo -e "${RED}Unknown command: $1${NC}" + echo "" + show_usage + exit 1 + ;; +esac + +echo "" +echo -e "${GREEN}Setup completed successfully!${NC}" +echo -e "${BLUE}For more information, see: docs/README-Reverse-Proxy-SSL.md${NC}" \ No newline at end of file diff --git a/scripts/setup-ssl.sh b/scripts/setup-ssl.sh new file mode 100755 index 0000000..552a74b --- /dev/null +++ b/scripts/setup-ssl.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# SSL Setup Script for Sibedas PBG Web +# This script sets up SSL certificates for the reverse proxy + +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 +DOMAIN="${DOMAIN:-sibedas.yourdomain.com}" +EMAIL="${EMAIL:-admin@yourdomain.com}" +SSL_TYPE="${SSL_TYPE:-self-signed}" + +echo -e "${BLUE}=== SSL Setup for Sibedas PBG Web ===${NC}" +echo -e "Domain: ${GREEN}$DOMAIN${NC}" +echo -e "Email: ${GREEN}$EMAIL${NC}" +echo -e "SSL Type: ${GREEN}$SSL_TYPE${NC}" +echo "" + +# Function to check if Docker is running +check_docker() { + if ! docker info > /dev/null 2>&1; then + echo -e "${RED}Error: Docker is not running${NC}" + exit 1 + fi +} + +# Function to check if containers are running +check_containers() { + if ! docker ps | grep -q sibedas_nginx_proxy; then + echo -e "${YELLOW}Warning: Reverse proxy container is not running${NC}" + echo -e "${YELLOW}Starting containers first...${NC}" + docker-compose up -d + sleep 10 + fi +} + +# Function to setup self-signed certificate +setup_self_signed() { + echo -e "${BLUE}Setting up self-signed SSL certificate...${NC}" + + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh self-signed + + echo -e "${GREEN}Self-signed certificate setup completed!${NC}" + echo -e "${YELLOW}Note: Self-signed certificates will show security warnings in browsers${NC}" +} + +# Function to setup Let's Encrypt certificate +setup_letsencrypt() { + echo -e "${BLUE}Setting up Let's Encrypt SSL certificate...${NC}" + + # Check if domain is accessible + echo -e "${YELLOW}Important: Make sure your domain $DOMAIN points to this server${NC}" + echo -e "${YELLOW}and ports 80 and 443 are accessible from the internet${NC}" + read -p "Press Enter to continue..." + + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh letsencrypt + + echo -e "${GREEN}Let's Encrypt certificate setup completed!${NC}" +} + +# Function to check certificate status +check_certificate() { + echo -e "${BLUE}Checking certificate status...${NC}" + + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh check +} + +# Function to renew certificate +renew_certificate() { + echo -e "${BLUE}Renewing SSL certificate...${NC}" + + docker exec sibedas_nginx_proxy /usr/local/bin/ssl-setup.sh renew + + echo -e "${GREEN}Certificate renewal completed!${NC}" +} + +# Function to show usage +show_usage() { + echo "Usage: $0 {setup|check|renew|self-signed|letsencrypt}" + echo "" + echo "Commands:" + echo " setup - Setup SSL certificate (default: self-signed)" + echo " check - Check certificate status" + echo " renew - Renew Let's Encrypt certificate" + echo " self-signed - Setup self-signed certificate" + echo " letsencrypt - Setup Let's Encrypt certificate" + echo "" + echo "Environment variables:" + echo " DOMAIN - Domain name (default: sibedas.yourdomain.com)" + echo " EMAIL - Email address for Let's Encrypt (default: admin@yourdomain.com)" + echo " SSL_TYPE - Type of SSL (letsencrypt or self-signed, default: self-signed)" + echo "" + echo "Examples:" + echo " DOMAIN=myapp.com EMAIL=admin@myapp.com $0 letsencrypt" + echo " $0 self-signed" + echo " $0 check" +} + +# Main script logic +case "${1:-setup}" in + "setup") + check_docker + check_containers + if [ "$SSL_TYPE" = "letsencrypt" ]; then + setup_letsencrypt + else + setup_self_signed + fi + ;; + "check") + check_docker + check_containers + check_certificate + ;; + "renew") + check_docker + check_containers + renew_certificate + ;; + "self-signed") + check_docker + check_containers + setup_self_signed + ;; + "letsencrypt") + check_docker + check_containers + setup_letsencrypt + ;; + *) + show_usage + exit 1 + ;; +esac + +echo "" +echo -e "${GREEN}SSL setup completed successfully!${NC}" +echo -e "${BLUE}You can now access your application at: https://$DOMAIN${NC}" \ No newline at end of file