Browse Source

feat(storage): pindah app_storage ke NAS QNAP via NFS mount, avatar simpan file fisik bukan base64

master
Budi Setiawan 3 weeks ago
parent
commit
5349a48853
  1. BIN
      server-connection/geonet-console-deploy.tar.gz
  2. 52
      server-connection/geonet-console/app/Http/Controllers/Api/V1/MeController.php
  3. 5
      server-connection/geonet-console/compose.yaml
  4. 7
      server-connection/geonet-console/docker/entrypoint.sh

BIN
server-connection/geonet-console-deploy.tar.gz

Binary file not shown.

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

@ -11,6 +11,8 @@ use App\Services\MailDeliveryService; @@ -11,6 +11,8 @@ use App\Services\MailDeliveryService;
use App\Support\ApiActor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use InvalidArgumentException;
use RuntimeException;
use Throwable;
@ -44,7 +46,10 @@ class MeController extends Controller @@ -44,7 +46,10 @@ class MeController extends Controller
$data['domain'] = $profile['domain'] ?? '';
$data['phone'] = $meta['phone'] ?? '';
$data['other_email'] = $meta['other_email'] ?? '';
$data['avatar'] = $meta['avatar'] ?? null;
$avatarPath = $meta['avatar'] ?? null;
$data['avatar'] = $avatarPath
? Storage::disk('public')->url($avatarPath)
: null;
}
return $this->ok($data);
@ -126,24 +131,51 @@ class MeController extends Controller @@ -126,24 +131,51 @@ class MeController extends Controller
}
$data = $request->validate([
'avatar' => ['nullable', 'string', 'max:262144'],
'avatar' => ['nullable', 'string', 'max:524288'],
]);
try {
$avatarValue = isset($data['avatar']) && $data['avatar'] !== ''
? $data['avatar']
: null;
$meta = $this->metadata->getUserMeta($actor->identifier);
$oldPath = $meta['avatar'] ?? null;
$avatarInput = isset($data['avatar']) && $data['avatar'] !== '' ? $data['avatar'] : null;
if ($avatarInput === null) {
// Hapus foto
if ($oldPath) {
Storage::disk('public')->delete($oldPath);
}
$this->metadata->saveAvatar($actor->identifier, null);
return $this->ok(['message' => 'Foto profil dihapus.', 'avatar_url' => null]);
}
if ($avatarValue !== null && ! preg_match('/^data:image\/(jpeg|png|webp|gif);base64,/', $avatarValue)) {
// Validasi format base64 data-URI
if (! preg_match('/^data:image\/(jpeg|png|webp);base64,(.+)$/s', $avatarInput, $m)) {
return $this->fail('Format avatar tidak valid. Harus base64 image (jpeg/png/webp).', 'invalid_format', 422);
}
$this->metadata->saveAvatar($actor->identifier, $avatarValue);
} catch (Throwable) {
return $this->fail('Gagal menyimpan avatar.', 'server_error', 500);
$imageData = base64_decode($m[2], strict: true);
if ($imageData === false || strlen($imageData) > 300 * 1024) {
return $this->fail('Ukuran gambar terlalu besar (maks 300KB).', 'too_large', 422);
}
$ext = $m[1] === 'jpeg' ? 'jpg' : $m[1];
$filename = 'avatars/' . Str::slug($actor->identifier) . '-' . Str::random(8) . '.' . $ext;
Storage::disk('public')->put($filename, $imageData);
// Hapus file lama jika ada
if ($oldPath && $oldPath !== $filename) {
Storage::disk('public')->delete($oldPath);
}
$this->metadata->saveAvatar($actor->identifier, $filename);
$avatarUrl = Storage::disk('public')->url($filename);
} catch (Throwable $e) {
return $this->fail('Gagal menyimpan avatar: ' . $e->getMessage(), 'server_error', 500);
}
return $this->ok(['message' => 'Avatar berhasil diperbarui.', 'avatar' => $avatarValue]);
return $this->ok(['message' => 'Foto profil berhasil diperbarui.', 'avatar_url' => $avatarUrl]);
}
public function testEmail(Request $request): JsonResponse

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

@ -45,7 +45,7 @@ services: @@ -45,7 +45,7 @@ services:
AUDIT_DB_USERNAME: ${AUDIT_DB_USERNAME:-audit}
AUDIT_DB_PASSWORD: ${AUDIT_DB_PASSWORD:-audit_secret_change_me}
volumes:
- app_storage:/var/www/html/storage
- /mnt/nas-gisportal/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,6 +58,3 @@ volumes: @@ -58,6 +58,3 @@ volumes:
audit_pg_data:
external: true
name: databaselist_audit_pg_data
app_storage:
external: true
name: databaselist_app_storage

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

@ -11,10 +11,15 @@ php artisan config:clear --quiet 2>/dev/null || true @@ -11,10 +11,15 @@ 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 bootstrap/cache database
mkdir -p storage/framework/{sessions,views,cache} storage/logs storage/app/public/avatars 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