diff --git a/.agent/workflows/database-guidelines.md b/.agent/workflows/database-guidelines.md new file mode 100644 index 0000000..e36db7a --- /dev/null +++ b/.agent/workflows/database-guidelines.md @@ -0,0 +1,34 @@ +--- +description: Panduan Manajemen Database (Multi-Database Architecture) +--- + +# Aturan & Panduan Database + +Proyek ini menggunakan arsitektur **Multi-Database** untuk memisahkan data User/App dengan data High-Security (Certificate Authority). + +## Arsitektur + +1. **Main Database Connection (`mysql`)** + * **Kegunaan**: Menyimpan data aplikasi umum (`users`, `tickets`, `certificates` (leaf), dll). + * **Reset Policy**: Boleh di-reset saat development (`php artisan migrate:fresh --seed`). + * **Dependency**: Terikat dengan logic aplikasi utama. + +2. **CA Database Connection (`mysql_ca`)** + * **Kegunaan**: KHUSUS untuk `ca_certificates` (Root & Intermediate CA). + * **Reset Policy**: **DILARANG RESET** sembarangan. Command `migrate:fresh` default TIDAK akan menyentuh database ini. + * **Driver**: Menggunakan `mysql` di Production (sama seperti Main DB), bukan SQLite atau D1 (kecuali ada instruksi spesifik). + +## Aturan Migrasi + +1. **Pembuatan Tabel Baru**: + * Tentukan tabel masuk ke kategori mana (App vs CA). + * Jika CA, gunakan `Schema::connection('mysql_ca')->create(...)`. + * Jika App, gunakan `Schema::create(...)` biasa. + +2. **Data Safety**: + * Sebelum menjalankan query raw atau operasi destructive, pastikan koneksi yang dipilih benar. + * Gunakan command `php artisan ca:migrate-data` hanya jika perlu memindahkan data antar database. + +## Cloudflare D1 +* Saat ini D1 **TIDAK DIGUNAKAN** untuk kompatibilitas penuh dengan server berbasis VPS/Hosting standar. +* Jangan mengusulkan migrasi ke D1 kecuali infrastruktur berpindah ke Cloudflare Workers sepenuhnya. diff --git a/.agent/workflows/deployment.md b/.agent/workflows/deployment.md new file mode 100644 index 0000000..4887f10 --- /dev/null +++ b/.agent/workflows/deployment.md @@ -0,0 +1,51 @@ +--- +description: SOP Deployment (CI/CD via aaPanel) +--- + +# Alur Kerja Deployment + +Proyek ini menggunakan **CI/CD Otomatis** via aaPanel Webhook yang terintegrasi dengan GitHub/Git. + +## 1. Automated Deployment (CI/CD) + +Setiap kali Anda melakukan push ke branch `main`, script webhook di server akan berjalan. +**Apa yang dilakukan script otomatis:** +1. `git pull origin main` +2. `composer install` & `npm install` + `vite build` +3. **Update Config:** Mengcopy isi `.env.production.editable` ke `.env` (Pastikan file editable sudah benar di repo!). +4. `php artisan migrate --force` (Main & CA Database). +5. `php artisan optimize`. + +**Script Reference:** +* **Repo (Public):** `scripts/deploy-webhook.example.sh` (Template aman, gunakan ini untuk copy-paste ke aaPanel lalu edit manual). +* **Local (Private):** `scripts/deploy-webhook.local.sh` (Backup pribadi Anda dengan path asli, ter-ignore oleh git). + +## 2. Manual Pre-Requisites (Sebelum Push) + +Sebelum Anda push code, pastikan: +1. **Environment Variables**: + * Jika ada perubahan config, update `.env.production.editable`. + * Ingat: Script akan menimpa `.env` server dengan isi `.env.production.editable`. +2. **Database**: + * Jika membuat DB baru (seperti kasus CA ini), pastikan database fisik sudah dibuat di server MySQL (`CREATE DATABASE ...`). + +## 3. Manual Post-Deployment (Intervensi Khusus) + +Script CI/CD tidak menangani edge-cases. Anda perlu masuk ke server (SSH) untuk kasus berikut: + +1. **Data Migration Khusus**: + * Kasus: Memisahkan table CA ke database baru. + * Action: Login SSH, lalu jalankan: + ```bash + cd /www/wwwroot/trustlab-api-ftp/trustlab-api.dyzulk.com + php artisan ca:migrate-data + ``` + +2. **Rollback**: + * Jika deploy gagal total, Anda mungkin perlu restore backup database manual via aaPanel atau `php artisan migrate:rollback`. + +## 4. Platform Lain (Non-aaPanel) +Jika berpindah dari aaPanel, adaptasi script `scripts/deploy-webhook.example.sh`: +* Ganti Path project (`PROJECT_PATH`). +* Ganti Path PHP Binary (`PHP_BIN`). +* Ganti mekanisme trigger (misal gunakan GitHub Actions, Jenkins, atau Laravel Forge). diff --git a/.agent/workflows/manage-env.md b/.agent/workflows/manage-env.md new file mode 100644 index 0000000..568c7c7 --- /dev/null +++ b/.agent/workflows/manage-env.md @@ -0,0 +1,48 @@ +--- +description: Memahami dan Mengelola Environment Variables (5-File System) +--- + +# Aturan Manajemen Environment Variables + +Proyek ini menggunakan sistem **5-File Environment** yang ketat untuk mencegah kesalahan konfigurasi produksi. AI dan Developer Wajib mengikuti aturan ini. + +## Struktur File + +1. **`.env`** (Local Development) + * Digunakan untuk pengembangan visual/lokal. + * Berisi kredensial lokal (localhost, root, dll). + * **Aturan:** Menjadi acuan utama *struktur* dan *urutan* key untuk file lainnya. + +2. **`.env.example.for.local`** (Template Local) + * Template untuk developer lain. + * Struktur HARUS sama persis dengan `.env`. + * Value kosong atau default aman. + +3. **`.env.example.for.production`** (Template Production) + * Gambaran konfigurasi produksi. + * Struktur HARUS sama persis dengan `.env`. + * Value disesuaikan untuk konteks produksi (misal `APP_ENV=production`, `APP_DEBUG=false`). + +4. **`.env.production.editable`** (Staging/Pre-Production) + * File ini berisi konfigurasi produksi yang *siap* untuk diedit/standardisasi. + * **CRITICAL:** Struktur dan urutan key HARUS 100% sama dengan `.env`. + * Berisi kredensial RILL/ASLI dari server produksi. + +5. **`.env.production.soft.copy`** (Snapshot Server - **READ ONLY**) + * Merupakan salinan langsung dari server saat ini. + * **DILARANG EDIT** file ini kecuali server aktual telah berubah. + * File ini digunakan sebagai validasi/referensi state server sekarang. + * Jangan menambahkan config baru di sini sebelum server di-update. + +## Workflow Perubahan Environment + +Jika Anda perlu menambahkan Variable baru (misal `DB_CA_...`): + +1. **Tambahkan di `.env`** lokal terlebih dahulu. +2. **Standardisasi urutan** di `.env.production.editable` (copy struktur `.env`, lalu isi value produksi). +3. **Update Template** `.env.example.for.local` dan `.env.example.for.production`. +4. **JANGAN SENTUH** `.env.production.soft.copy` (biarkan apa adanya sampai deployment selesai dan snapshot baru diambil). + +## Prompting AI +Untuk memastikan AI mengerti konteks ini, mintalah: +> "Baca aturan environment di `.agent/workflows/manage-env.md` sebelum melakukan perubahan pada file .env" diff --git a/.env.example.for.local b/.env.example.for.local new file mode 100644 index 0000000..b408530 --- /dev/null +++ b/.env.example.for.local @@ -0,0 +1,128 @@ +# This .env for example local development +APP_NAME=TrustLab +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_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=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +# CA DB Connection +DB_CA_CONNECTION=mysql_ca +DB_CA_HOST=127.0.0.1 +DB_CA_PORT=3306 +DB_CA_DATABASE=trustlab_ca +DB_CA_USERNAME=root +DB_CA_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=localhost + +SANCTUM_STATEFUL_DOMAINS=localhost:3000,127.0.0.1:3000 + +BROADCAST_CONNECTION=reverb +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=smtp +MAIL_HOST=lab.dyzulk.com +MAIL_PORT=587 +MAIL_USERNAME=noreply@lab.dyzulk.com +MAIL_PASSWORD= +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="noreply@lab.dyzulk.com" +MAIL_FROM_NAME="${APP_NAME}" + +MAIL_SUPPORT_MAILER=smtp +MAIL_SUPPORT_HOST=lab.dyzulk.com +MAIL_SUPPORT_PORT=587 +MAIL_SUPPORT_USERNAME=support@lab.dyzulk.com +MAIL_SUPPORT_PASSWORD= +MAIL_SUPPORT_ENCRYPTION=tls +MAIL_SUPPORT_FROM_ADDRESS="support@lab.dyzulk.com" +MAIL_SUPPORT_FROM_NAME="${APP_NAME} Support" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +R2_ACCESS_KEY_ID= +R2_SECRET_ACCESS_KEY= +R2_BUCKET= +R2_PRIVATE_BUCKET= +R2_ENDPOINT= +R2_URL= + +VITE_APP_NAME="${APP_NAME}" + +FRONTEND_URL=http://localhost:3000 + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID= +REVERB_APP_KEY= +REVERB_APP_SECRET= +REVERB_HOST="localhost" +REVERB_PORT=8080 +REVERB_SCHEME=http + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" + +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_DEV_CLIENT_ID= +GITHUB_DEV_CLIENT_SECRET= +GITHUB_DEV_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_PROD_CLIENT_ID= +GITHUB_PROD_CLIENT_SECRET= +GITHUB_PROD_REDIRECT_URI=${APP_URL}/api/auth/github/callback + + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=${APP_URL}/api/auth/google/callback + +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= \ No newline at end of file diff --git a/.env.example.for.production b/.env.example.for.production new file mode 100644 index 0000000..bf3de84 --- /dev/null +++ b/.env.example.for.production @@ -0,0 +1,128 @@ +# This .env for example production +APP_NAME=TrustLab +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=https://trustlab-api.dyzulk.com + +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=error + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=trustlab +DB_USERNAME=trustlab +DB_PASSWORD= + +# CA DB Connection +DB_CA_CONNECTION=mysql_ca +DB_CA_HOST=127.0.0.1 +DB_CA_PORT=3306 +DB_CA_DATABASE=trustlab_ca +DB_CA_USERNAME= +DB_CA_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=.dyzulk.com + +SANCTUM_STATEFUL_DOMAINS=trustlab.dyzulk.com + +BROADCAST_CONNECTION=reverb +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=smtp +MAIL_HOST=lab.dyzulk.com +MAIL_PORT=587 +MAIL_USERNAME=noreply@lab.dyzulk.com +MAIL_PASSWORD= +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="noreply@lab.dyzulk.com" +MAIL_FROM_NAME="${APP_NAME}" + +MAIL_SUPPORT_MAILER=smtp +MAIL_SUPPORT_HOST=lab.dyzulk.com +MAIL_SUPPORT_PORT=587 +MAIL_SUPPORT_USERNAME=support@lab.dyzulk.com +MAIL_SUPPORT_PASSWORD= +MAIL_SUPPORT_ENCRYPTION=tls +MAIL_SUPPORT_FROM_ADDRESS="support@lab.dyzulk.com" +MAIL_SUPPORT_FROM_NAME="${APP_NAME} Support" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +R2_ACCESS_KEY_ID= +R2_SECRET_ACCESS_KEY= +R2_BUCKET= +R2_PRIVATE_BUCKET= +R2_ENDPOINT= +R2_URL= + +VITE_APP_NAME="${APP_NAME}" + +FRONTEND_URL=https://trustlab.dyzulk.com + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID= +REVERB_APP_KEY= +REVERB_APP_SECRET= +REVERB_HOST="trustlab-api.dyzulk.com" +REVERB_PORT=443 +REVERB_SCHEME=https + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" + +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_DEV_CLIENT_ID= +GITHUB_DEV_CLIENT_SECRET= +GITHUB_DEV_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_PROD_CLIENT_ID= +GITHUB_PROD_CLIENT_SECRET= +GITHUB_PROD_REDIRECT_URI=${APP_URL}/api/auth/github/callback + + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=${APP_URL}/api/auth/google/callback + +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= diff --git a/.gitignore b/.gitignore index 55d5875..a16319e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ .DS_Store .env .env.backup -.env.example.for.local -.env.example.for.production .env.production.editable .env.production.soft.copy .phpactor.json @@ -28,3 +26,4 @@ Thumbs.db *.sql *.sqlite .env.testing +scripts/deploy-webhook.local.sh diff --git a/app/Console/Commands/MigrateCaCertificates.php b/app/Console/Commands/MigrateCaCertificates.php new file mode 100644 index 0000000..4c2fb6e --- /dev/null +++ b/app/Console/Commands/MigrateCaCertificates.php @@ -0,0 +1,80 @@ +info('Starting CA data migration...'); + + // Check if source table exists + if (!DB::connection('mysql')->getSchemaBuilder()->hasTable('ca_certificates')) { + $this->error('Source table "ca_certificates" does not exist in the default connection.'); + return 1; + } + + // Check if target table is empty + $count = CaCertificate::count(); + if ($count > 0 && !$this->option('force')) { + $this->error("Target table is not empty (contains $count records). Use --force to proceed."); + return 1; + } + + // Fetch from old DB + $oldCerts = DB::connection('mysql')->table('ca_certificates')->get(); + + $this->info("Found {$oldCerts->count()} certificates to migrate."); + + $bar = $this->output->createProgressBar($oldCerts->count()); + $bar->start(); + + foreach ($oldCerts as $cert) { + // We use the Model to insert into the new DB (since it's now bound to 'mysql_ca') + // Using replicate() or manual array creation + + $data = (array) $cert; + + // Ensure we don't duplicate if it already exists (upsert-like behavior or strict check) + if (CaCertificate::where('uuid', $data['uuid'])->exists()) { + if ($this->option('force')) { + // Update existing + CaCertificate::where('uuid', $data['uuid'])->update($data); + } else { + // Skip + } + } else { + CaCertificate::create($data); + } + + $bar->advance(); + } + + $bar->finish(); + $this->newLine(); + $this->info('Data migration completed successfully.'); + + return 0; + } +} diff --git a/app/Models/CaCertificate.php b/app/Models/CaCertificate.php index 8c69c01..7690c0b 100644 --- a/app/Models/CaCertificate.php +++ b/app/Models/CaCertificate.php @@ -11,6 +11,8 @@ class CaCertificate extends Model public $incrementing = false; protected $keyType = 'string'; + protected $connection = 'mysql_ca'; + protected $fillable = [ 'uuid', 'ca_type', diff --git a/config/database.php b/config/database.php index c57fa63..64f3882 100644 --- a/config/database.php +++ b/config/database.php @@ -63,6 +63,26 @@ return [ ]) : [], ], + 'mysql_ca' => [ + 'driver' => 'mysql', + 'url' => env('DB_CA_URL'), + 'host' => env('DB_CA_HOST', '127.0.0.1'), + 'port' => env('DB_CA_PORT', '3306'), + 'database' => env('DB_CA_DATABASE', 'trustlab_ca'), + 'username' => env('DB_CA_USERNAME', 'root'), + 'password' => env('DB_CA_PASSWORD', ''), + 'unix_socket' => env('DB_CA_SOCKET', ''), + 'charset' => env('DB_CA_CHARSET', 'utf8mb4'), + 'collation' => env('DB_CA_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + 'mariadb' => [ 'driver' => 'mariadb', 'url' => env('DB_URL'), diff --git a/database/migrations/2026_01_06_000001_create_ca_certificates_table_new_db.php b/database/migrations/2026_01_06_000001_create_ca_certificates_table_new_db.php new file mode 100644 index 0000000..700c13b --- /dev/null +++ b/database/migrations/2026_01_06_000001_create_ca_certificates_table_new_db.php @@ -0,0 +1,40 @@ +create('ca_certificates', function (Blueprint $table) { + $table->string('uuid', 32)->primary(); + $table->string('ca_type'); // root, intermediate_4096, intermediate_2048 + $table->longText('cert_content')->nullable(); + $table->longText('key_content')->nullable(); + $table->string('serial_number')->nullable(); + $table->string('common_name')->nullable(); + $table->string('organization')->nullable(); + $table->dateTime('valid_from')->nullable(); + $table->dateTime('valid_to')->nullable(); + + // Tracking + $table->unsignedBigInteger('download_count')->default(0); + $table->timestamp('last_downloaded_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::connection('mysql_ca')->dropIfExists('ca_certificates'); + } +}; diff --git a/scripts/deploy-webhook.example.sh b/scripts/deploy-webhook.example.sh new file mode 100644 index 0000000..cb23996 --- /dev/null +++ b/scripts/deploy-webhook.example.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +# ========================================================================= +# TRUSTLAB DEPLOYMENT SCRIPT (EXAMPLE) +# ========================================================================= +# CATATAN PENTING: +# Script ini adalah CONTOH/TEMPLATE untuk digunakan di aaPanel Webhook. +# Jangan jalankan script ini langsung dari repository jika belum dikonfigurasi. +# +# CARA PAKAI DI AAPANEL: +# 1. Buka App Store > Webhook (atau Git Manager di versi baru). +# 2. Add Webhook > Script. +# 3. Copy-paste isi file ini ke dalam kolom Script di aaPanel. +# 4. SESUAIKAN variable di bawah ini dengan konfigurasi server Anda. +# ========================================================================= + +# --- 1. KONFIGURASI SERVER (WAJIB DIEDIT DI AAPANEL) --- +# Ganti dengan path project Anda yang sebenarnya +PROJECT_PATH="/www/wwwroot/your-project.com" + +# Ganti dengan path PHP binary Anda (sesuai versi php) +PHP_BIN="/www/server/php/83/bin/php" + +# ========================================================================= +# CONFIGURATION & ENVIRONMENT (JANGAN UBAH DI BAWAH INI KECUALI PAHAM) +# ========================================================================= +export HOME=/root +export COMPOSER_HOME=/root/.composer +export PATH=$PATH:/usr/local/bin:/usr/bin:/bin + +# --- CONFIG TELEGRAM --- +# Load from .env locally on server if available +if [ -f .env ]; then + export $(grep -v '^#' .env | xargs) +fi + +# Pastikan TELEGRAM_BOT_TOKEN dan TELEGRAM_CHAT_ID ada di .env server Anda +BOT_TOKEN="${TELEGRAM_BOT_TOKEN}" +CHAT_ID="${TELEGRAM_CHAT_ID}" + +send_telegram() { + local message="$1" + if [ -n "$BOT_TOKEN" ] && [ -n "$CHAT_ID" ]; then + curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \ + -d chat_id="$CHAT_ID" \ + -d text="$message" \ + -d parse_mode="HTML" > /dev/null + else + echo "โš ๏ธ Telegram credentials missing, skipping notification." + fi +} + +# ========================================================================= +# START DEPLOYMENT +# ========================================================================= +echo "๐Ÿš€ Starting Deployment..." +send_telegram "โณ Deployment Started%0A%0A๐Ÿš€ Project: TrustLab API%0A๐Ÿ“… Date: $(date)" + +set -e + +# Safety check directory +if [ ! -d "$PROJECT_PATH" ]; then + echo "โŒ Error: Project path $PROJECT_PATH does not exist." + exit 1 +fi + +git config --global --add safe.directory "$PROJECT_PATH" +cd "$PROJECT_PATH" + +trap 'send_telegram "โŒ Deployment FAILED!%0A%0Aโš ๏ธ Check server logs untuk detail.%0A๐Ÿ“… Date: $(date)"; exit 1' ERR + +# 3. Pull & Clean +echo "๐Ÿ“ฅ Pulling latest code..." +git pull origin main + +echo "๐Ÿงน Cleaning untracked files..." +git clean -fd + +# 4. PHP Dependencies +echo "๐Ÿ“ฆ Updating Composer dependencies..." +$PHP_BIN /usr/bin/composer install --no-dev --optimize-autoloader --no-interaction + +# 5. Frontend Assets +echo "๐Ÿ“ฆ Building frontend assets..." +npm install + +echo "๐Ÿ”ง Fixing permissions..." +find node_modules -type f \( -path "*/bin/*" -o -path "*/.bin/*" \) -exec chmod +x {} \; +if [ -d "node_modules/@esbuild/linux-x64/bin" ]; then + chmod +x node_modules/@esbuild/linux-x64/bin/esbuild +fi + +rm -rf public/build +echo "๐Ÿ— Running Vite build..." +npx vite build + +echo "๐Ÿงน Pruning dev dependencies..." +npm prune --omit=dev + +# 6. Environment Setup +if [ -f .env.production.editable ]; then + echo "๐Ÿ“„ Updating .env from .env.production.editable..." + cp .env.production.editable .env +elif [ ! -f .env ]; then + cp .env.production.example .env +fi + +# 7. Laravel Optimizations +echo "โšก Optimizing Laravel..." +$PHP_BIN artisan optimize:clear +$PHP_BIN artisan migrate --force + +# NEW: Conditional CA Data Migration +# $PHP_BIN artisan ca:migrate-data + +$PHP_BIN artisan config:cache +$PHP_BIN artisan route:cache +$PHP_BIN artisan view:cache + +echo "โœ… Deployment SUCCESS!" +send_telegram "โœ… Deployment Success!%0A%0A๐Ÿ“ฆ Project: TrustLab API%0A๐Ÿ“… Date: $(date)"