From 43cdba7e9bd9c0c448602bd1caeb6f84db4edbee Mon Sep 17 00:00:00 2001 From: rbsetiawan Date: Thu, 25 Jun 2026 10:02:51 +0700 Subject: [PATCH] feat(storage): migrasi avatar ke QNAP QuObjects S3 (OSS), hapus NFS bind mount --- server-connection/geonet-console/.env.example | 11 ++++ .../Http/Controllers/Api/V1/MeController.php | 10 ++-- server-connection/geonet-console/compose.yaml | 9 ++- .../geonet-console/composer.json | 3 +- .../geonet-console/config/filesystems.php | 55 +++++++++++++++++++ .../geonet-console/docker/entrypoint.sh | 7 +-- 6 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 server-connection/geonet-console/config/filesystems.php diff --git a/server-connection/geonet-console/.env.example b/server-connection/geonet-console/.env.example index ebc5690..0847ac6 100644 --- a/server-connection/geonet-console/.env.example +++ b/server-connection/geonet-console/.env.example @@ -143,5 +143,16 @@ GEONETAGENT_DB_PASSWORD= +# Object Storage — QNAP QuObjects (S3-compatible, port 8010) +# Access key format: : +OSS_ACCESS_KEY_ID=super-apps:0Jq7KbF31eJzd4k3LSXN +OSS_SECRET_ACCESS_KEY=change-me +OSS_BUCKET=super-apps +OSS_ENDPOINT=http://10.100.1.10:8010 +OSS_REGION=us-east-1 +OSS_PUBLIC_URL=http://file.gisportal.id:8010 + + + TZ=Asia/Jakarta diff --git a/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php b/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php index 4e9491f..3278428 100644 --- a/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php +++ b/server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php @@ -48,7 +48,7 @@ class MeController extends Controller $data['other_email'] = $meta['other_email'] ?? ''; $avatarPath = $meta['avatar'] ?? null; $data['avatar'] = $avatarPath - ? Storage::disk('public')->url($avatarPath) + ? Storage::disk('oss')->url($avatarPath) : null; } @@ -142,7 +142,7 @@ class MeController extends Controller if ($avatarInput === null) { // Hapus foto if ($oldPath) { - Storage::disk('public')->delete($oldPath); + Storage::disk('oss')->delete($oldPath); } $this->metadata->saveAvatar($actor->identifier, null); return $this->ok(['message' => 'Foto profil dihapus.', 'avatar_url' => null]); @@ -161,16 +161,16 @@ class MeController extends Controller $ext = $m[1] === 'jpeg' ? 'jpg' : $m[1]; $filename = 'avatars/' . Str::slug($actor->identifier) . '-' . Str::random(8) . '.' . $ext; - Storage::disk('public')->put($filename, $imageData); + Storage::disk('oss')->put($filename, $imageData, 'public'); // Hapus file lama jika ada if ($oldPath && $oldPath !== $filename) { - Storage::disk('public')->delete($oldPath); + Storage::disk('oss')->delete($oldPath); } $this->metadata->saveAvatar($actor->identifier, $filename); - $avatarUrl = Storage::disk('public')->url($filename); + $avatarUrl = Storage::disk('oss')->url($filename); } catch (Throwable $e) { return $this->fail('Gagal menyimpan avatar: ' . $e->getMessage(), 'server_error', 500); } diff --git a/server-connection/geonet-console/compose.yaml b/server-connection/geonet-console/compose.yaml index dcba212..a326f54 100644 --- a/server-connection/geonet-console/compose.yaml +++ b/server-connection/geonet-console/compose.yaml @@ -44,8 +44,14 @@ services: AUDIT_DB_DATABASE: ${AUDIT_DB_DATABASE:-databaselist_audit} AUDIT_DB_USERNAME: ${AUDIT_DB_USERNAME:-audit} AUDIT_DB_PASSWORD: ${AUDIT_DB_PASSWORD:-audit_secret_change_me} + OSS_ACCESS_KEY_ID: ${OSS_ACCESS_KEY_ID} + OSS_SECRET_ACCESS_KEY: ${OSS_SECRET_ACCESS_KEY} + OSS_BUCKET: ${OSS_BUCKET:-super-apps} + OSS_ENDPOINT: ${OSS_ENDPOINT:-http://10.100.1.10:8010} + OSS_REGION: ${OSS_REGION:-us-east-1} + OSS_PUBLIC_URL: ${OSS_PUBLIC_URL:-http://file.gisportal.id:8010} volumes: - - /mnt/nas-gisportal/storage:/var/www/html/storage + - app_storage:/var/www/html/storage - ./database:/var/www/html/database healthcheck: test: ["CMD-SHELL", "curl -sf http://127.0.0.1/up >/dev/null || exit 1"] @@ -58,3 +64,4 @@ volumes: audit_pg_data: external: true name: databaselist_audit_pg_data + app_storage: diff --git a/server-connection/geonet-console/composer.json b/server-connection/geonet-console/composer.json index 128e890..9a422d4 100644 --- a/server-connection/geonet-console/composer.json +++ b/server-connection/geonet-console/composer.json @@ -7,7 +7,8 @@ "firebase/php-jwt": "^7.0", "laravel/framework": "^11.31", "laravel/passport": "^13.7", - "laravel/tinker": "^2.10" + "laravel/tinker": "^2.10", + "league/flysystem-aws-s3-v3": "^3.0" }, "autoload": { "psr-4": { diff --git a/server-connection/geonet-console/config/filesystems.php b/server-connection/geonet-console/config/filesystems.php new file mode 100644 index 0000000..b3dd63e --- /dev/null +++ b/server-connection/geonet-console/config/filesystems.php @@ -0,0 +1,55 @@ + env('FILESYSTEM_DISK', 'local'), + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], + + // QNAP QuObjects (S3-compatible) — primary object storage + 'oss' => [ + 'driver' => 's3', + 'key' => env('OSS_ACCESS_KEY_ID'), + 'secret' => env('OSS_SECRET_ACCESS_KEY'), + 'region' => env('OSS_REGION', 'us-east-1'), + 'bucket' => env('OSS_BUCKET', 'super-apps'), + 'endpoint' => env('OSS_ENDPOINT', 'http://10.100.1.10:8010'), + 'use_path_style_endpoint' => true, + 'url' => env('OSS_PUBLIC_URL'), + 'visibility' => 'public', + 'throw' => true, + ], + + ], + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/server-connection/geonet-console/docker/entrypoint.sh b/server-connection/geonet-console/docker/entrypoint.sh index 07d60b1..87bb0bf 100644 --- a/server-connection/geonet-console/docker/entrypoint.sh +++ b/server-connection/geonet-console/docker/entrypoint.sh @@ -11,15 +11,10 @@ php artisan config:clear --quiet 2>/dev/null || true php artisan route:clear --quiet 2>/dev/null || true php artisan view:clear --quiet 2>/dev/null || true -mkdir -p storage/framework/{sessions,views,cache} storage/logs storage/app/public/avatars bootstrap/cache database +mkdir -p storage/framework/{sessions,views,cache} storage/logs bootstrap/cache database chown -R www-data:www-data storage bootstrap/cache 2>/dev/null || true chmod -R 775 storage bootstrap/cache 2>/dev/null || true -# Buat symlink public/storage -> storage/app/public agar file bisa diakses via URL -if [ ! -L public/storage ]; then - ln -sf /var/www/html/storage/app/public public/storage 2>/dev/null || true -fi - if [ ! -f storage/oauth-private.key ]; then php artisan passport:keys --force --quiet 2>/dev/null || true fi