Browse Source

feat(storage): migrasi avatar ke QNAP QuObjects S3 (OSS), hapus NFS bind mount

master
Budi Setiawan 3 weeks ago
parent
commit
43cdba7e9b
  1. 11
      server-connection/geonet-console/.env.example
  2. 10
      server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php
  3. 9
      server-connection/geonet-console/compose.yaml
  4. 3
      server-connection/geonet-console/composer.json
  5. 55
      server-connection/geonet-console/config/filesystems.php
  6. 7
      server-connection/geonet-console/docker/entrypoint.sh

11
server-connection/geonet-console/.env.example

@ -143,5 +143,16 @@ GEONETAGENT_DB_PASSWORD= @@ -143,5 +143,16 @@ GEONETAGENT_DB_PASSWORD=
# Object Storage — QNAP QuObjects (S3-compatible, port 8010)
# Access key format: <storage-space>:<key-id>
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

10
server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php

@ -48,7 +48,7 @@ class MeController extends Controller @@ -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 @@ -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 @@ -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);
}

9
server-connection/geonet-console/compose.yaml

@ -44,8 +44,14 @@ services: @@ -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: @@ -58,3 +64,4 @@ volumes:
audit_pg_data:
external: true
name: databaselist_audit_pg_data
app_storage:

3
server-connection/geonet-console/composer.json

@ -7,7 +7,8 @@ @@ -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": {

55
server-connection/geonet-console/config/filesystems.php

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
<?php
return [
'default' => 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'),
],
];

7
server-connection/geonet-console/docker/entrypoint.sh

@ -11,15 +11,10 @@ php artisan config:clear --quiet 2>/dev/null || true @@ -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

Loading…
Cancel
Save