add sync to leader dashboard new from google spreadsheet

This commit is contained in:
arifal hidayat
2025-06-14 02:15:49 +07:00
parent baed8cc487
commit 780ba60224
22 changed files with 933 additions and 644 deletions

76
.env.local.backup Normal file
View File

@@ -0,0 +1,76 @@
APP_NAME=SIBEDAS-PBG
APP_ENV=local
APP_KEY=base64:xqCpwixWKqgu1Ca22gFizoOt44p7h+cgTOKuhS/P0Jw=
APP_DEBUG=true
APP_TIMEZONE=Asia/Jakarta
APP_URL=http://localhost:8000
API_URL=http://localhost:8000
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mariadb
DB_HOST=db
DB_PORT=3306
DB_DATABASE=sibedas_db
DB_USERNAME=root
DB_PASSWORD=root
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
SIMBG_HOST="https://simbg.pu.go.id/"
SIMBG_EMAIL="dputr@bandungkab.go.id"
SIMBG_PASSWORD="Simbg123"
API_KEY_GOOGLE="AIzaSyBxfEShFkKmykkc7RJR3lVzkQ_xGHK3qr0"
SPREAD_SHEET_ID="1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY"
OPENAI_API_KEY="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"

View File

@@ -1,180 +0,0 @@
# 🚀 Panduan Build untuk Production
## 📋 Masalah yang Diselesaikan
Aplikasi ini memiliki **banyak file JS dan CSS** yang harus dibangun semuanya karena:
- Setiap halaman memerlukan file JS/CSS yang spesifik
- Jika ada file yang hilang, halaman akan error (404 Not Found)
- Server dengan RAM terbatas sering mengalami "killed" saat build
## 🎯 Solusi yang Tersedia
### 1. **Build di Local (RECOMMENDED) 🏆**
```bash
# Build di local computer Anda
npm run build:local
# Upload ke server
./deploy.sh username server.com /path/to/project
```
**Kelebihan:**
- ✅ Tidak ada batasan memory
- ✅ Build lebih cepat
- ✅ Semua file terjamin ter-build
### 2. **Build di Server dengan Optimasi**
```bash
# Option A: Build dengan memory optimization
npm run build:prod
# Option B: Build dengan retry mechanism
npm run build:chunked
# Option C: Build manual dengan script
./build-production.sh
```
### 3. **Setup Server untuk Build**
```bash
# Setup swap memory dan optimasi (run dengan sudo)
sudo ./server-setup.sh
# Kemudian build
npm run build:prod
```
## 📁 File yang Akan Dibangun
Semua file ini **WAJIB** ada karena dibutuhkan oleh halaman-halaman aplikasi:
### CSS Files:
- `resources/scss/style.scss` - Main stylesheet
- `resources/scss/icons.scss` - Icons
- `resources/scss/components/_*.scss` - Components
- `resources/scss/dashboards/_*.scss` - Dashboard styles
- `resources/scss/pages/quick-search/*.scss` - Quick search pages
- Third-party CSS (Quill, Flatpickr, GridJS, dll)
### JS Files:
- **Core:** `app.js`, `config.js`, `dashboard.js`
- **Pages:** `chart.js`, `form-*.js`, `table-*.js`, `maps-*.js`
- **Data:** `data-advertisements.js`, `data-umkm.js`, `data-tourisms.js`
- **Settings:** `syncronize.js`, `general-settings.js`
- **Dashboards:** `bigdata.js`, `pbg.js`, `potentials/*.js`
- **Users & Roles:** `users/*.js`, `roles/*.js`, `menus/*.js`
- **Reports:** `growth-report.js`, `tourisms/index.js`
- **Dan semua file lainnya sesuai kebutuhan halaman**
## ⚙️ Konfigurasi Build
### Memory Optimization
```javascript
// vite.config.production.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['bootstrap', 'moment', 'axios'],
charts: ['apexcharts'],
maps: ['leaflet', 'jsvectormap', 'gmaps'],
ui: ['sweetalert2', 'flatpickr', 'quill']
}
},
maxParallelFileOps: 1 // Untuk server dengan RAM terbatas
}
}
})
```
### Memory Limits
```bash
# Untuk server dengan RAM 2GB+
NODE_OPTIONS="--max-old-space-size=2048"
# Untuk server dengan RAM 1GB
NODE_OPTIONS="--max-old-space-size=1024"
```
## 🔧 Troubleshooting
### Build Terkill (Killed)
```bash
# Solusi 1: Build di local
npm run build:local
./deploy.sh
# Solusi 2: Tambah swap memory
sudo ./server-setup.sh
# Solusi 3: Gunakan build chunked
npm run build:chunked
```
### File JS/CSS Tidak Ditemukan
```bash
# Pastikan semua file ada di vite.config.production.js
# Jangan hapus file apapun dari daftar input!
# Verifikasi build
find public/build -name "*.js" | wc -l
find public/build -name "*.css" | wc -l
```
### Server Kehabisan Memory
```bash
# Check memory usage
free -h
# Add swap file
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```
## 📦 Deployment Flow
### Local Build → Server Deploy
```bash
# 1. Di local
npm ci
npm run build:local
# 2. Edit deploy.sh dengan info server
# 3. Deploy
./deploy.sh username server.com /path/to/project
```
### Server Build
```bash
# 1. Setup server
sudo ./server-setup.sh
# 2. Install dependencies
npm ci --production=false
# 3. Build
npm run build:chunked
# 4. Optimize Laravel
php artisan config:cache
php artisan route:cache
php artisan view:cache
```
## ⚠️ Catatan Penting
1. **JANGAN HAPUS FILE APAPUN** dari `vite.config.production.js`
2. **SEMUA FILE WAJIB ADA** karena dibutuhkan oleh halaman-halaman aplikasi
3. **BUILD DI LOCAL** adalah solusi terbaik untuk server dengan RAM terbatas
4. **BACKUP** selalu sebelum deploy ke production
## 🎉 Success Indicators
Build berhasil jika:
- ✅ File `public/build/manifest.json` ada
- ✅ Folder `public/build/assets/` berisi banyak file JS/CSS
- ✅ Tidak ada error 404 saat mengakses halaman
- ✅ Semua halaman dapat memuat JS/CSS yang dibutuhkan

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Services\ServiceGoogleSheet;
use Illuminate\Console\Command;
class ScrapingLeaderData extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:scraping-leader-data';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Scraping leader data from google spreadsheet and save to database';
/**
* Execute the console command.
*/
public function handle()
{
$service_google_sheet = app(ServiceGoogleSheet::class);
$service_google_sheet->sync_leader_data();
$this->info('Leader data synced successfully');
}
}

View File

@@ -21,4 +21,13 @@ class BigDataController extends Controller
{
return view('dashboards.pbg');
}
public function leader()
{
$latest_import_datasource = ImportDatasource::latest()->first();
$latest_created = $latest_import_datasource ?
$latest_import_datasource->created_at->format("j F Y H:i:s") : null;
$menus = Menu::all();
return view('dashboards.leader', compact('latest_created', 'menus'));
}
}

View File

@@ -1,14 +1,16 @@
<?php
namespace App\Services;
use App\Models\BigdataResume;
use App\Models\DataSetting;
use App\Models\ImportDatasource;
use App\Models\PbgTaskGoogleSheet;
use Carbon\Carbon;
use Exception;
use Google_Client;
use Google_Service_Sheets;
use Log;
use Google\Client as Google_Client;
use Google\Service\Sheets as Google_Service_Sheets;
use Illuminate\Support\Facades\Log;
class ServiceGoogleSheet
{
protected $client;
@@ -210,6 +212,78 @@ class ServiceGoogleSheet
}
}
public function sync_leader_data(){
$import_datasource = ImportDatasource::create([
'message' => 'Processing leader data',
'status' => 'processing',
'start_time' => now(),
'failed_uuid' => null
]);
try {
$sections = [
'TARGET_PAD' => "TARGET PAD 2024",
'KEKURANGAN_POTENSI' => "DEVIASI TARGET DENGAN POTENSI TOTAL BERKAS",
'TOTAL_POTENSI_BERKAS' => "•TOTAL BERKAS 2025",
'BELUM_TERVERIFIKASI' => "•BERKAS AKTUAL BELUM TERVERIFIKASI (POTENSI):",
'TERVERIFIKASI' => "•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2025:",
'NON_USAHA' => "•NON USAHA: HUNIAN, SOSBUD, KEAGAMAAN",
'USAHA' => "•USAHA: USAHA, CAMPURAN, KOLEKTIF, PRASARANA"
];
$result = [];
foreach ($sections as $key => $identifier) {
$values = $this->get_values_from_section(2, $identifier, [10, 11]);
if (!empty($values)) {
$result[$key] = [
'identifier' => $identifier,
'total' => $values[0] ?? null, // index 0 untuk total/jumlah
'nominal' => $values[1] ?? null // index 1 untuk nominal
];
}
}
Log::info("Leader data synced", ['result' => $result]);
BigdataResume::create([
'import_datasource_id' => $import_datasource->id,
'year' => now()->year,
// USAHA
'business_count' => $this->convertToInteger($result['USAHA']['total'] ?? null) ?? 0,
'business_sum' => $this->convertToDecimal($result['USAHA']['nominal'] ?? null) ?? 0,
// NON USAHA
'non_business_count' => $this->convertToInteger($result['NON_USAHA']['total'] ?? null) ?? 0,
'non_business_sum' => $this->convertToDecimal($result['NON_USAHA']['nominal'] ?? null) ?? 0,
// TERVERIFIKASI
'verified_count' => $this->convertToInteger($result['TERVERIFIKASI']['total'] ?? null) ?? 0,
'verified_sum' => $this->convertToDecimal($result['TERVERIFIKASI']['nominal'] ?? null) ?? 0,
// BELUM TERVERIFIKASI
'non_verified_count' => $this->convertToInteger($result['BELUM_TERVERIFIKASI']['total'] ?? null) ?? 0,
'non_verified_sum' => $this->convertToDecimal($result['BELUM_TERVERIFIKASI']['nominal'] ?? null) ?? 0,
// TOTAL POTENSI BERKAS
'potention_count' => $this->convertToInteger($result['TOTAL_POTENSI_BERKAS']['total'] ?? null) ?? 0,
'potention_sum' => $this->convertToDecimal($result['TOTAL_POTENSI_BERKAS']['nominal'] ?? null) ?? 0,
]);
$import_datasource->update([
'status' => 'success',
'response_body' => json_encode($result),
'message' => 'Leader data synced',
'finish_time' => now()
]);
return $result;
} catch (\Exception $e) {
Log::error("Error syncing leader data", ['error' => $e->getMessage()]);
$import_datasource->update([
'status' => 'failed',
'message' => 'Leader data sync failed',
'finish_time' => now()
]);
throw $e;
}
}
public function get_big_resume_data(){
try {
$sheet_big_data = $this->get_data_by_sheet();
@@ -261,9 +335,71 @@ class ServiceGoogleSheet
return!empty($values)? $values : [];
}
/**
* Get specific values from a row that contains a specific text/section identifier
* @param int $no_sheet Sheet number (0-based)
* @param string $section_identifier Text to search for in the row
* @param array $column_indices Array of column indices to extract values from
* @return array Array of values from specified columns, or empty array if section not found
*/
private function get_values_from_section($no_sheet = 1, $section_identifier, $column_indices = []) {
try {
$sheet_data = $this->get_data_by_sheet($no_sheet);
if (empty($sheet_data)) {
Log::warning("No data found in sheet", ['sheet' => $no_sheet]);
return [];
}
// Search for the row containing the section identifier
$target_row = null;
foreach ($sheet_data as $row_index => $row) {
if (is_array($row)) {
foreach ($row as $cell) {
if (is_string($cell) && strpos($cell, $section_identifier) !== false) {
$target_row = $row;
break 2; // Break out of both loops
}
}
}
}
if ($target_row === null) {
Log::warning("Section not found", ['section_identifier' => $section_identifier]);
return [];
}
// Extract values from specified column indices
$extracted_values = [];
foreach ($column_indices as $col_index) {
if (isset($target_row[$col_index])) {
$value = trim($target_row[$col_index]);
$extracted_values[] = $value !== '' ? $value : null;
} else {
$extracted_values[] = null;
}
}
Log::info("Values extracted from section", [
'section_identifier' => $section_identifier,
'column_indices' => $column_indices,
'extracted_values' => $extracted_values
]);
return $extracted_values;
} catch (\Exception $e) {
Log::error("Error getting values from section", [
'error' => $e->getMessage(),
'section_identifier' => $section_identifier,
'sheet' => $no_sheet
]);
return [];
}
}
private function convertToInteger($value) {
// Check if the value is an empty string, and return null if true
if (trim($value) === "") {
// Check if the value is null or empty string, and return null if true
if ($value === null || trim($value) === "") {
return null;
}

View File

@@ -1,92 +0,0 @@
#!/bin/bash
# Build script bertahap untuk menghindari memory overload
# Tetap membangun SEMUA file yang diperlukan tapi secara bertahap
echo "🔨 Starting chunked production build..."
# Set memory limit
export NODE_OPTIONS="--max-old-space-size=1536"
export NODE_ENV=production
# Clean previous build
echo "🧹 Cleaning previous build..."
rm -rf public/build/*
# Fungsi untuk build dengan retry
build_with_retry() {
local config_file=$1
local attempt=1
local max_attempts=3
while [ $attempt -le $max_attempts ]; do
echo "🔄 Build attempt $attempt of $max_attempts..."
if vite build --config $config_file; then
echo "✅ Build successful on attempt $attempt"
return 0
else
echo "❌ Build failed on attempt $attempt"
if [ $attempt -lt $max_attempts ]; then
echo "⏳ Waiting 5 seconds before retry..."
sleep 5
# Clear memory caches
sync && echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
fi
attempt=$((attempt + 1))
fi
done
return 1
}
echo "📦 Building all assets with memory optimization..."
# Try building with production config first
if build_with_retry "vite.config.production.js"; then
echo "🎉 Build completed successfully!"
echo "📊 Build summary:"
du -sh public/build/* 2>/dev/null || echo "Build files created"
# Count generated files
echo "📁 Generated files:"
find public/build -type f | wc -l | xargs echo "Total files:"
else
echo "❌ Build failed after all attempts!"
echo ""
echo "💡 Alternative solutions:"
echo " 1. ✨ Build locally: npm run build:local"
echo " 2. 🔧 Increase server memory/swap"
echo " 3. ☁️ Use GitHub Actions CI/CD"
echo " 4. 🚀 Use deployment services (Vercel, Netlify)"
echo ""
echo "🏠 For local build and deploy:"
echo " npm run build:local"
echo " ./deploy.sh your-username your-server.com /path/to/project"
exit 1
fi
echo ""
echo "🔍 Verifying all essential files are built..."
# Check for essential files
essential_files=(
"public/build/manifest.json"
"public/build/assets"
)
missing_files=()
for file in "${essential_files[@]}"; do
if [ ! -e "$file" ]; then
missing_files+=("$file")
fi
done
if [ ${#missing_files[@]} -eq 0 ]; then
echo "✅ All essential files are present!"
else
echo "⚠️ Missing files:"
printf ' %s\n' "${missing_files[@]}"
fi

View File

@@ -1,43 +0,0 @@
#!/bin/bash
# Production build script dengan optimasi memory
# Untuk server dengan resource terbatas
echo "🔨 Starting production build with memory optimization..."
# Set Node.js memory limit (adjust sesuai RAM server)
# Untuk server dengan banyak file, gunakan memory yang lebih besar
export NODE_OPTIONS="--max-old-space-size=2048 --max-semi-space-size=1024"
# Use production vite config
export NODE_ENV=production
# Clean previous build
echo "🧹 Cleaning previous build..."
rm -rf public/build/*
# Build with limited resources
echo "📦 Building assets with memory optimization..."
# Option 1: Build with custom config
vite build --config vite.config.production.js
# Option 2: Alternative - build in chunks (uncomment if needed)
# echo "Building CSS files first..."
# vite build --config vite.config.production.js --mode css-only
#
# echo "Building JS files..."
# vite build --config vite.config.production.js --mode js-only
if [ $? -eq 0 ]; then
echo "✅ Build completed successfully!"
echo "📊 Build summary:"
du -sh public/build/*
else
echo "❌ Build failed!"
echo "💡 Try these solutions:"
echo " 1. Increase server memory"
echo " 2. Build locally and upload files"
echo " 3. Use swap memory (temporary solution)"
exit 1
fi

BIN
build.zip

Binary file not shown.

View File

@@ -30,7 +30,7 @@ class MenuSeeder extends Seeder
"sort_order" => 2,
"children" => [
[
"name" => "Dashboard Pimpinan",
"name" => "Dashboard Pimpinan SIMBG",
"url" => "dashboard.home",
"icon" => null,
"sort_order" => 1,
@@ -67,6 +67,12 @@ class MenuSeeder extends Seeder
"icon" => null,
"sort_order" => 4,
],
[
"name" => "Dashboard Pimpinan",
"url" => "dashboard.leader",
"icon" => null,
"sort_order" => 5,
],
],
],
[

View File

@@ -1,85 +0,0 @@
#!/bin/bash
# Debug script untuk diagnosa permission issues
# Jalankan di server untuk check masalah permission
PROJECT_PATH=${1:-"/var/www/pupr"}
echo "🔍 Debugging permissions for: $PROJECT_PATH"
echo "================================================="
# Check if path exists
if [ ! -d "$PROJECT_PATH" ]; then
echo "❌ Project path not found: $PROJECT_PATH"
exit 1
fi
cd "$PROJECT_PATH"
echo "📊 Current User & Groups:"
echo "Current user: $(whoami)"
echo "Web server user: $(ps aux | grep -E 'apache|nginx|httpd' | grep -v grep | head -1 | awk '{print $1}')"
echo ""
echo "📁 Directory Ownership & Permissions:"
echo "Project root:"
ls -la . | head -5
echo ""
echo "Storage directory:"
ls -la storage/
echo ""
echo "Storage/framework:"
ls -la storage/framework/ 2>/dev/null || echo "storage/framework/ not found"
echo ""
echo "Bootstrap/cache:"
ls -la bootstrap/cache/ 2>/dev/null || echo "bootstrap/cache/ not found"
echo ""
echo "🔐 Permission Analysis:"
# Check critical directories
directories=("storage" "storage/framework" "storage/framework/views" "storage/framework/cache" "storage/logs" "bootstrap/cache")
for dir in "${directories[@]}"; do
if [ -d "$dir" ]; then
perm=$(stat -c "%a" "$dir")
owner=$(stat -c "%U:%G" "$dir")
echo "$dir: $perm ($owner)"
else
echo "$dir: NOT FOUND"
fi
done
echo ""
echo "🧪 Write Test:"
# Test write permissions
if [ -w "storage/framework/" ]; then
echo "✅ storage/framework/ is writable"
# Try creating a test file
if touch storage/framework/test_write_$(date +%s).tmp 2>/dev/null; then
echo "✅ Can create files in storage/framework/"
rm -f storage/framework/test_write_*.tmp
else
echo "❌ Cannot create files in storage/framework/"
fi
else
echo "❌ storage/framework/ is NOT writable"
fi
echo ""
echo "⚙️ SELinux Status (if applicable):"
if command -v getenforce &> /dev/null; then
echo "SELinux: $(getenforce)"
if [ "$(getenforce)" = "Enforcing" ]; then
echo "⚠️ SELinux is enforcing - may need additional configuration"
echo "Try: sudo setsebool -P httpd_unified on"
fi
else
echo "SELinux not installed"
fi
echo ""
echo "📋 Recommended Actions:"
echo "1. Fix ownership: sudo chown -R www-data:www-data $PROJECT_PATH"
echo "2. Fix permissions: sudo chmod -R 775 $PROJECT_PATH/storage/"
echo "3. Clear cache: php artisan view:clear"
echo "4. If SELinux: sudo setsebool -P httpd_unified on"

View File

@@ -1,59 +0,0 @@
#!/bin/bash
# Deploy script untuk build di local dan upload ke server
# Usage: ./deploy.sh [server-user] [server-host] [server-path]
# Default values (ganti sesuai server Anda)
SERVER_USER=${1:-"root"}
SERVER_HOST=${2:-"157.245.48.15"}
SERVER_PATH=${3:-"/var/www/pupr"}
echo "🚀 Starting deployment process..."
# 1. Clean previous build
echo "🧹 Cleaning previous build..."
rm -rf public/build/*
# 2. Install dependencies (jika belum)
echo "📦 Installing dependencies..."
npm ci --production=false
# 3. Build project
echo "🔨 Building project..."
npm run build
# Check if build was successful
if [ $? -ne 0 ]; then
echo "❌ Build failed!"
exit 1
fi
echo "✅ Build completed successfully!"
# 4. Upload build files to server
echo "📤 Uploading build files to server..."
rsync -avz --progress --delete public/build/ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/public/build/
# 5. Upload other necessary files (optional)
echo "📤 Uploading other files..."
rsync -avz --progress \
--exclude='node_modules' \
--exclude='.git' \
--exclude='public/build' \
--exclude='storage/logs/*' \
--exclude='bootstrap/cache/*' \
./ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/
echo "🔧 Fixing permissions on server..."
ssh $SERVER_USER@$SERVER_HOST "cd $SERVER_PATH && chmod +x fix-permissions.sh && sudo ./fix-permissions.sh $SERVER_PATH"
echo "📦 Installing composer dependencies on server..."
ssh $SERVER_USER@$SERVER_HOST "cd $SERVER_PATH && composer install --no-dev --optimize-autoloader"
echo "🎉 Deployment completed successfully!"
echo ""
echo "✅ Permissions have been automatically fixed!"
echo "✅ Composer dependencies installed!"
echo "✅ Laravel caches optimized!"
echo ""
echo "🌐 Your application should now be accessible without permission errors!"

View File

@@ -0,0 +1,41 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
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
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/pupr/storage/logs/queue-worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopasgroup=true
killasgroup=true
user=www-data
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
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/pupr/storage/logs/scheduler.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopasgroup=true
killasgroup=true
user=www-data
priority=20
[group:laravel]
programs=laravel-queue-worker,laravel-scheduler
priority=999

View File

@@ -15,4 +15,17 @@ stdout_logfile=/var/www/storage/logs/worker.log
stopasgroup=true
killasgroup=true
user=www-data
priority=10
priority=10
[program:laravel-scheduler]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan schedule:work
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/scheduler.log
stopasgroup=true
killasgroup=true
user=www-data
priority=20

View File

@@ -1,73 +0,0 @@
#!/bin/bash
# Script untuk fix permissions Laravel setelah deployment
# Jalankan di server production
PROJECT_PATH=${1:-"/var/www/pupr"}
echo "🔧 Fixing Laravel permissions for: $PROJECT_PATH"
# Check if path exists
if [ ! -d "$PROJECT_PATH" ]; then
echo "❌ Project path not found: $PROJECT_PATH"
echo "Usage: ./fix-permissions.sh /path/to/your/project"
exit 1
fi
cd "$PROJECT_PATH"
echo "📁 Setting correct ownership..."
# Set ownership to web server user
sudo chown -R www-data:www-data .
echo "📂 Setting directory permissions..."
# Set directory permissions
sudo find . -type d -exec chmod 755 {} \;
echo "📄 Setting file permissions..."
# Set file permissions
sudo find . -type f -exec chmod 644 {} \;
echo "🔐 Setting special permissions for Laravel..."
# Storage and cache directories need write permissions
sudo chmod -R 775 storage/
sudo chmod -R 775 bootstrap/cache/
# Make sure these directories exist
sudo mkdir -p storage/framework/views
sudo mkdir -p storage/framework/cache
sudo mkdir -p storage/framework/sessions
sudo mkdir -p storage/logs
sudo mkdir -p storage/app/public
# Set ownership for storage and bootstrap/cache
sudo chown -R www-data:www-data storage/
sudo chown -R www-data:www-data bootstrap/cache/
echo "🔑 Setting executable permissions for Artisan..."
sudo chmod +x artisan
echo "🧹 Clearing Laravel caches..."
# Clear all caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear
# Recreate caches with correct permissions
php artisan config:cache
php artisan route:cache
php artisan view:cache
echo "📋 Checking permissions..."
echo "Storage permissions:"
ls -la storage/
echo ""
echo "Framework permissions:"
ls -la storage/framework/
echo ""
echo "✅ Permissions fixed successfully!"
echo "💡 If you still get permission errors, also run:"
echo " sudo setsebool -P httpd_can_network_connect on"
echo " sudo setsebool -P httpd_unified on"

View File

@@ -0,0 +1,378 @@
import Big from "big.js";
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
import InitDatePicker from "../utils/InitDatePicker.js";
class BigData {
async init() {
try {
new InitDatePicker(
"#datepicker-dashboard-bigdata",
this.handleChangeDate.bind(this)
).init();
// Load initial data
this.updateData("latest");
} catch (error) {
console.error("Error initializing data:", error);
}
}
handleChangeDate(filterDate) {
if (!filterDate) return;
this.updateData(filterDate);
}
async updateData(filterDate) {
try {
this.resumeBigData = await this.getBigDataResume(filterDate);
this.initChartTargetPAD(filterDate);
this.initChartUsaha();
this.initChartNonUsaha();
this.initChartTotalPotensi();
this.initChartVerificationDocuments();
this.initChartNonVerificationDocuments();
this.initChartKekuranganPotensi();
this.initChartRealisasiTerbitPBG();
this.initChartMenungguKlikDPMPTSP();
this.initChartProsesDinasTeknis();
this.initChartPotensiTataRuang();
} catch (e) {
console.error(e);
}
}
async getBigDataResume(filterByDate) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterByDate}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']")
.content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
initChartTargetPAD(filterDate) {
const year =
filterDate === "latest"
? new Date().getFullYear()
: new Date(filterDate).getFullYear();
document
.querySelectorAll(".document-title.chart-target-pad")
.forEach((element) => {
element.innerText = `Target PAD ${year}`;
});
document
.querySelectorAll(".document-count.chart-target-pad")
.forEach((element) => {
element.innerText = ``;
});
document
.querySelectorAll(".document-total.chart-target-pad")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
this.resumeBigData.target_pad.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-target-pad")
.forEach((element) => {
element.innerText = `${this.resumeBigData.target_pad.percentage}%`;
});
}
initChartTotalPotensi() {
// const countAll = this.resultDataTotal.countData ?? 0;
document
.querySelectorAll(".document-count.chart-total-potensi")
.forEach((element) => {
// element.innerText = `${countAll}`;
element.innerText = `${this.resumeBigData.total_potensi.count}`;
});
document
.querySelectorAll(".document-total.chart-total-potensi")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.bigTotalPotensi.toString()
this.resumeBigData.total_potensi.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-total-potensi")
.forEach((element) => {
// element.innerText = `${this.resultPercentage}%`;
element.innerText = `${this.resumeBigData.total_potensi.percentage}%`;
});
}
initChartVerificationDocuments() {
document
.querySelectorAll(".document-count.chart-berkas-terverifikasi")
.forEach((element) => {
// element.innerText = `${this.dataVerification.count}`;
element.innerText = `${this.resumeBigData.verified_document.count}`;
});
document
.querySelectorAll(".document-total.chart-berkas-terverifikasi")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.bigTotalVerification.toString()
this.resumeBigData.verified_document.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-berkas-terverifikasi")
.forEach((element) => {
// element.innerText = `${this.percetageResultVerification}%`;
element.innerText = `${this.resumeBigData.verified_document.percentage}%`;
});
}
initChartNonVerificationDocuments() {
document
.querySelectorAll(
".document-count.chart-berkas-belum-terverifikasi"
)
.forEach((element) => {
// element.innerText = `${this.dataNonVerification.count}`;
element.innerText = `${this.resumeBigData.non_verified_document.count}`;
});
document
.querySelectorAll(
".document-total.chart-berkas-belum-terverifikasi"
)
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.bigTotalNonVerification.toString()
this.resumeBigData.non_verified_document.sum.toString()
)}`;
});
document
.querySelectorAll(
".small-percentage.chart-berkas-belum-terverifikasi"
)
.forEach((element) => {
// element.innerText = `${this.percentageResultNonVerification}%`;
element.innerText = `${this.resumeBigData.non_verified_document.percentage}%`;
});
}
initChartUsaha() {
document
.querySelectorAll(".document-count.chart-business")
.forEach((element) => {
// element.innerText = `${this.dataBusiness.count}`;
element.innerText = `${this.resumeBigData.business_document.count}`;
});
document
.querySelectorAll(".document-total.chart-business")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.bigTotalBusiness.toString()
this.resumeBigData.business_document.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-business")
.forEach((element) => {
// element.innerText = `${this.percentageResultBusiness}%`;
element.innerText = `${this.resumeBigData.business_document.percentage}%`;
});
}
initChartNonUsaha() {
document
.querySelectorAll(".document-count.chart-non-business")
.forEach((element) => {
// element.innerText = `${this.dataNonBusiness.count}`;
element.innerText = `${this.resumeBigData.non_business_document.count}`;
});
document
.querySelectorAll(".document-total.chart-non-business")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.bigTotalNonBusiness.toString()
this.resumeBigData.non_business_document.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-non-business")
.forEach((element) => {
// element.innerText = `${this.percentageResultNonBusiness}%`;
element.innerText = `${this.resumeBigData.non_business_document.percentage}%`;
});
}
initChartKekuranganPotensi() {
document
.querySelectorAll(".document-count.chart-kekurangan-potensi")
.forEach((element) => {
element.innerText = ``;
});
document
.querySelectorAll(".document-total.chart-kekurangan-potensi")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.totalKekuranganPotensi.toString()
this.resumeBigData.kekurangan_potensi.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-kekurangan-potensi")
.forEach((element) => {
// element.innerText = `${this.percentageKekuranganPotensi}%`;
element.innerText = `${this.resumeBigData.kekurangan_potensi.percentage}%`;
});
}
initChartRealisasiTerbitPBG() {
document
.querySelectorAll(".document-count.chart-realisasi-tebit-pbg")
.forEach((element) => {
// element.innerText = `${this.dataCountRealisasiTerbit}`;
element.innerText = `${this.resumeBigData.realisasi_terbit.count}`;
});
document
.querySelectorAll(".document-total.chart-realisasi-tebit-pbg")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.dataSumRealisasiTerbit
this.resumeBigData.realisasi_terbit.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg")
.forEach((element) => {
element.innerText = `${this.resumeBigData.realisasi_terbit.percentage}%`;
});
}
initChartMenungguKlikDPMPTSP() {
document
.querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp")
.forEach((element) => {
// element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`;
element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.count}`;
});
document
.querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.dataSumMenungguKlikDPMPTSP
this.resumeBigData.menunggu_klik_dpmptsp.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp")
.forEach((element) => {
element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.percentage}%`;
});
}
initChartProsesDinasTeknis() {
document
.querySelectorAll(".document-count.chart-proses-dinas-teknis")
.forEach((element) => {
// element.innerText = `${this.dataCountProsesDinasTeknis}`;
element.innerText = `${this.resumeBigData.proses_dinas_teknis.count}`;
});
document
.querySelectorAll(".document-total.chart-proses-dinas-teknis")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
// this.dataSumProsesDinasTeknis
this.resumeBigData.proses_dinas_teknis.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-proses-dinas-teknis")
.forEach((element) => {
element.innerText = `${this.resumeBigData.proses_dinas_teknis.percentage}%`;
});
}
initChartPotensiTataRuang() {
document
.querySelectorAll(".document-count.chart-potensi-tata-ruang")
.forEach((element) => {
element.innerText = `${this.resumeBigData.tata_ruang.count}`;
});
document
.querySelectorAll(".document-total.chart-potensi-tata-ruang")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
this.resumeBigData.tata_ruang.sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-potensi-tata-ruang")
.forEach((element) => {
element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`;
});
}
}
document.addEventListener("DOMContentLoaded", async function (e) {
await new BigData().init();
});
// function resizeDashboard() {
// //Target Width
// let targetElement = document.getElementById("dashboard-fixed-wrapper");
// let targetWidth = targetElement.offsetWidth;
// //console.log("TARGET ",targetWidth);
// //Real Object Width
// let dashboardElement = document.getElementById("dashboard-fixed-container");
// let dashboardWidth = 1110; //dashboardElement.offsetWidth;
// //console.log("CURRENT ",dashboardWidth);
// if (targetWidth > dashboardWidth) {
// targetWidth = dashboardWidth;
// }
// dashboardElement.style.transformOrigin = "left top";
// dashboardElement.style.transition = "transform 0.2s ease-in-out";
// dashboardElement.style.transform =
// "scale(" + (targetWidth / dashboardWidth).toFixed(2) + ")";
// //console.log("SCALE ", (targetWidth/dashboardWidth).toFixed(2));
// }
// window.addEventListener("load", function () {
// resizeDashboard();
// });
// window.addEventListener("resize", function () {
// resizeDashboard();
// });
function resizeDashboard() {
let targetElement = document.getElementById("dashboard-fixed-wrapper");
let dashboardElement = document.getElementById("dashboard-fixed-container");
let targetWidth = targetElement.offsetWidth;
let dashboardWidth = 1110;
let scaleFactor = (targetWidth / dashboardWidth).toFixed(2);
// Prevent scaling beyond 1 (100%) to avoid overflow
scaleFactor = Math.min(scaleFactor, 1);
dashboardElement.style.transformOrigin = "left top";
dashboardElement.style.transition = "transform 0.2s ease-in-out";
dashboardElement.style.transform = `scale(${scaleFactor})`;
// Ensure horizontal scrolling is allowed if necessary
document.body.style.overflowX = "auto";
}
window.addEventListener("load", resizeDashboard);
window.addEventListener("resize", resizeDashboard);

View File

@@ -71,7 +71,6 @@ class SyncronizeTask {
data.data.map((item) => [
item.id,
item.message,
item.response_body,
item.status,
item.start_time,
item.duration,

View File

@@ -0,0 +1,187 @@
@extends('layouts.vertical', ['subtitle' => 'Dashboards'])
@section('css')
@vite(['resources/scss/dashboards/_bigdata.scss'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan'])
<div id="dashboard-fixed-wrapper" class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
<h2 class="text-danger m-0">
ANALISA BIG DATA PROSES PBG <br>
MELALUI APLIKASI SIBEDAS PBG
</h2>
<div class="text-black text-end d-flex flex-column align-items-end me-3">
<span class="fs-5">Terakhir di update - {{$latest_created}}</span>
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
</div>
</div>
</div>
<div id="dashboard-fixed-container" class="row" style="width:1110px;height:770px;position:relative;margin:auto;">
@component('components.circle', [
'document_title' => 'Kekurangan Potensi',
'document_color' => '#ff5757',
'document_type' => '',
'document_id' => 'chart-kekurangan-potensi',
'visible_small_circle' => true,
'style' => 'top:150px;'
])
@endcomponent
@component('components.circle', [
'document_title' => 'Target PAD',
'document_color' => '#204f6b',
'document_type' => '',
'document_id' => 'chart-target-pad',
'visible_small_circle' => true,
'style' => 'left:200px;',
'document_url' => route('data-settings.index', ['menu_id' => $menus->where('url','data-settings.index')->first()->id])
])
@endcomponent
<div class="square dia-top-left-bottom-right" style="top:150px;left:350px;">
</div>
<div class="square dia-top-right-bottom-left" style="top:150px;left:150px;">
</div>
@component('components.circle', [
'document_title' => 'Total Potensi Berkas',
'document_color' => '#0097b3',
'document_type' => 'Pemohon',
'document_id' => 'chart-total-potensi',
'visible_small_circle' => true,
'style' => 'left:400px;top:150px;',
'document_url' => route('pbg-task.index', ['menu_id' => $menus->where('url','pbg-task.index')->first()->id, 'filter' => 'all'])
])
@endcomponent
<div class="square dia-top-right-bottom-left" style="top:300px;left:350px;">
</div>
<div class="square dia-top-left-bottom-right" style="top:300px;left:550px;">
</div>
@component('components.circle', [
'document_title' => 'Perkiraan Potensi PBG Dari Tata Ruang',
'document_color' => '#ed9d2e',
'document_type' => '',
'document_id' => 'chart-potensi-tata-ruang',
'visible_small_circle' => true,
'style' => 'left:600px;',
'document_url' => route('web-spatial-plannings.index', ['menu_id' => $menus->where('url','web-spatial-plannings.index')->first()->id])
])
@endcomponent
<div class="square dia-top-right-bottom-left" style="top:150px;left:550px;">
</div>
@component('components.circle', [
'document_title' => 'Non Usaha',
'document_color' => '#399787',
'document_type' => 'Berkas',
'document_id' => 'chart-non-business',
'visible_small_circle' => true,
'style' => 'left:900px;top:150px;',
'document_url' => route('pbg-task.index', ['menu_id' => $menus->where('url','pbg-task.index')->first()->id, 'filter' => 'non-business'])
])
@endcomponent
@component('components.circle', [
'document_title' => 'Usaha',
'document_color' => '#5e7c89',
'document_type' => 'Berkas',
'document_id' => 'chart-business',
'visible_small_circle' => true,
'style' => 'left:900px;top:400px;',
'document_url' => route('pbg-task.index', ['menu_id' => $menus->where('url','pbg-task.index')->first()->id, 'filter' => 'business'])
])
@endcomponent
@component('components.circle', [
'document_title' => 'Berkas Terverifikasi',
'document_color' => '#5170ff',
'document_type' => 'Berkas',
'document_id' => 'chart-berkas-terverifikasi',
'visible_small_circle' => true,
'style' => 'top:300px;left:200px;',
'document_url' => route('pbg-task.index', ['menu_id' => $menus->where('url','pbg-task.index')->first()->id, 'filter' => 'verified'])
])
@endcomponent
<div class="square dia-top-right-bottom-left" style="top:500px;left:200px;width:50px">
</div>
<div class="square dia-top-left-bottom-right" style="top:450px;left:350px;width:500px;height:200px;">
</div>
@component('components.circle', [
'document_title' => 'Berkas Belum Terverifikasi',
'document_color' => '#5170ff',
'document_type' => 'Berkas',
'document_id' => 'chart-berkas-belum-terverifikasi',
'visible_small_circle' => true,
'style' => 'top:300px;left:600px;',
'document_url' => route('pbg-task.index', ['menu_id' => $menus->where('url','pbg-task.index')->first()->id, 'filter' => 'non-verified'])
])
@endcomponent
<div class="square dia-top-right-bottom-left" style="top:200px;left:750px;width:250px;height:150px;">
</div>
<div class="square dia-top-left-bottom-right" style="top:400px;left:750px;width:250px;height:150px;">
</div>
@component('components.circle',[
'document_title' => 'Realisasi Terbit PBG',
'document_color' => '#8cc540',
'document_type' => 'Berkas',
'document_id' => 'chart-realisasi-tebit-pbg',
'visible_small_circle' => true,
'style' => 'top:550px;left:100px;',
'document_url' => 'https://docs.google.com/spreadsheets/d/1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY/edit?gid=1514195399#gid=1514195399'
])
@endcomponent
<div class="square" style="top:650px;left:200px;width:250px;height:2px;background-color:black;">
</div>
@component('components.circle',[
'document_title' => 'Menunggu Klik DPMPTSP',
'document_color' => '#00bf61',
'document_type' => 'Berkas',
'document_id' => 'chart-menunggu-klik-dpmptsp',
'visible_small_circle' => true,
'style' => 'top:550px;left:400px',
'document_url' => 'https://docs.google.com/spreadsheets/d/1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY/edit?gid=1514195399#gid=1514195399'
])
@endcomponent
<div class="square" style="top:650px;left:600px;width:250px;height:2px;background-color:black;">
</div>
@component('components.circle',[
'document_title' => 'Berproses Di Dinas Teknis',
'document_color' => '#737373',
'document_type' => 'Berkas',
'document_id' => 'chart-proses-dinas-teknis',
'visible_small_circle' => true,
'style' => 'top:550px;left:700px',
'document_url' => 'https://docs.google.com/spreadsheets/d/1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY/edit?gid=1514195399#gid=1514195399'
])
@endcomponent
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/dashboards/bigdata.js'])
@endsection

View File

@@ -8,4 +8,5 @@ Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote')->hourly();
Schedule::command("app:scraping-data")->dailyAt("00:00");
Schedule::command("app:scraping-leader-data")->dailyAt("00:00");
Schedule::command("app:scraping-data")->dailyAt("00:30");

View File

@@ -51,6 +51,7 @@ Route::group(['middleware' => 'auth'], function(){
//dashboards
Route::group(['prefix' => '/dashboards'], function(){
Route::get('/bigdata', [BigDataController::class, 'index'])->name('dashboard.home');
Route::get('/leader', [BigDataController::class, 'leader'])->name('dashboard.leader');
Route::get('/dashboard-pbg', [BigDataController::class, 'pbg'])->name('dashboard.pbg');
Route::get('/lack-of-potential', [LackOfPotentialController::class, 'lack_of_potential'])->name('dashboard.lack_of_potential');
Route::get('/maps', [GoogleApisController::class, 'index'])->name('dashboard.maps');

View File

@@ -1,61 +0,0 @@
#!/bin/bash
# Server setup untuk optimasi memory dan build
# Jalankan dengan sudo di server production
echo "🔧 Setting up server for optimized building..."
# 1. Check current memory
echo "📊 Current memory status:"
free -h
# 2. Setup swap file (temporary solution for low memory)
echo "💾 Setting up swap file (1GB)..."
if [ ! -f /swapfile ]; then
fallocate -l 1G /swapfile
chmod 600 /swapfile
mkswap /swapfile
sysctl vm.swappiness=10
echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo 'vm.swappiness=10' >> /etc/sysctl.conf
swapon /swapfile
echo "✅ Swap file created and activated"
else
echo " Swap file already exists"
fi
# 3. Optimize Node.js for production
echo "⚙️ Optimizing Node.js..."
npm config set fund false
npm config set audit false
# 4. Install PM2 untuk process management
echo "📦 Installing PM2..."
npm install -g pm2
# 5. Create PM2 ecosystem file for build process
cat > ecosystem.config.js << 'EOF'
module.exports = {
apps: [{
name: 'build-app',
script: 'npm',
args: 'run build:prod',
instances: 1,
autorestart: false,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
NODE_OPTIONS: '--max-old-space-size=1024'
}
}]
}
EOF
echo "✅ Server setup completed!"
echo ""
echo "📝 Now you can:"
echo " 1. Build with memory limit: npm run build:prod"
echo " 2. Build with PM2: pm2 start ecosystem.config.js"
echo " 3. Monitor: pm2 monit"
echo " 4. Check memory: free -h"

View File

@@ -4,26 +4,26 @@ import path from "path";
export default defineConfig({
server: {
host: '0.0.0.0',
host: "0.0.0.0",
hmr: {
host: 'localhost'
host: "localhost",
},
watch: {
usePolling: true
}
usePolling: true,
},
},
build: {
outDir: 'public/build',
assetsDir: 'assets',
manifest: true,
outDir: "public/build",
assetsDir: "assets",
manifest: "manifest.json",
rollupOptions: {
output: {
manualChunks: undefined,
entryFileNames: 'assets/[name].js',
chunkFileNames: 'assets/[name].js',
assetFileNames: 'assets/[name].[ext]'
}
}
entryFileNames: "assets/[name].js",
chunkFileNames: "assets/[name].js",
assetFileNames: "assets/[name].[ext]",
},
},
},
resolve: {
alias: {
@@ -82,6 +82,7 @@ export default defineConfig({
"resources/js/dashboards/bigdata.js",
"resources/js/dashboards/potentials/inside_system.js",
"resources/js/dashboards/potentials/outside_system.js",
"resources/js/dashboards/leader.js",
// roles
"resources/js/roles/index.js",
"resources/js/roles/create.js",

View File

@@ -4,46 +4,46 @@ import path from "path";
export default defineConfig({
server: {
host: '0.0.0.0',
host: "0.0.0.0",
hmr: {
host: 'localhost'
host: "localhost",
},
watch: {
usePolling: true
}
usePolling: true,
},
},
build: {
outDir: 'public/build',
assetsDir: 'assets',
manifest: true,
outDir: "public/build",
assetsDir: "assets",
manifest: "manifest.json",
// Optimasi untuk server dengan resource terbatas
minify: 'terser',
minify: "terser",
chunkSizeWarningLimit: 1000,
rollupOptions: {
output: {
manualChunks: {
// Split vendor chunks untuk mengurangi memory usage
vendor: ['bootstrap', 'moment', 'axios'],
charts: ['apexcharts'],
maps: ['leaflet', 'jsvectormap', 'gmaps'],
ui: ['sweetalert2', 'flatpickr', 'quill', 'dropzone'],
forms: ['gridjs', 'simplebar'],
vendor: ["bootstrap", "moment", "axios"],
charts: ["apexcharts"],
maps: ["leaflet", "jsvectormap", "gmaps"],
ui: ["sweetalert2", "flatpickr", "quill", "dropzone"],
forms: ["gridjs", "simplebar"],
// Group by functionality to reduce memory usage per chunk
dashboards: [
'resources/js/dashboards/bigdata.js',
'resources/js/dashboards/pbg.js',
'resources/js/dashboards/potentials/inside_system.js',
'resources/js/dashboards/potentials/outside_system.js'
"resources/js/dashboards/bigdata.js",
"resources/js/dashboards/pbg.js",
"resources/js/dashboards/potentials/inside_system.js",
"resources/js/dashboards/potentials/outside_system.js",
],
data: [
'resources/js/data/umkm/data-umkm.js',
'resources/js/data/tourisms/data-tourisms.js',
'resources/js/data/advertisements/data-advertisements.js'
]
"resources/js/data/umkm/data-umkm.js",
"resources/js/data/tourisms/data-tourisms.js",
"resources/js/data/advertisements/data-advertisements.js",
],
},
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
entryFileNames: "assets/[name]-[hash].js",
chunkFileNames: "assets/[name]-[hash].js",
assetFileNames: "assets/[name]-[hash].[ext]",
},
// Optimasi memory - lebih konservatif
maxParallelFileOps: 1,
@@ -54,9 +54,9 @@ export default defineConfig({
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
drop_debugger: true,
},
},
},
resolve: {
alias: {
@@ -115,6 +115,7 @@ export default defineConfig({
"resources/js/dashboards/bigdata.js",
"resources/js/dashboards/potentials/inside_system.js",
"resources/js/dashboards/potentials/outside_system.js",
"resources/js/dashboards/leader.js",
// roles
"resources/js/roles/index.js",
"resources/js/roles/create.js",
@@ -186,6 +187,6 @@ export default defineConfig({
],
// Limit memory usage
esbuild: {
target: 'es2015'
}
});
target: "es2015",
},
});