diff --git a/server-connection/geonet-console-deploy.tar.gz b/server-connection/geonet-console-deploy.tar.gz index 7c4b667..8797084 100644 Binary files a/server-connection/geonet-console-deploy.tar.gz and b/server-connection/geonet-console-deploy.tar.gz differ 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 e57e347..fbed28c 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 @@ -44,6 +44,7 @@ class MeController extends Controller $data['domain'] = $profile['domain'] ?? ''; $data['phone'] = $meta['phone'] ?? ''; $data['other_email'] = $meta['other_email'] ?? ''; + $data['avatar'] = $meta['avatar'] ?? null; } return $this->ok($data); @@ -115,6 +116,36 @@ class MeController extends Controller return $this->ok(['message' => 'Password berhasil diubah.']); } + public function updateAvatar(Request $request): JsonResponse + { + /** @var ApiActor $actor */ + $actor = $request->attributes->get('api_actor'); + + if ($actor->type !== 'ldap_jwt') { + return $this->fail('Hanya akun LDAP yang dapat mengubah avatar.', 'forbidden', 403); + } + + $data = $request->validate([ + 'avatar' => ['nullable', 'string', 'max:262144'], + ]); + + try { + $avatarValue = isset($data['avatar']) && $data['avatar'] !== '' + ? $data['avatar'] + : null; + + if ($avatarValue !== null && ! preg_match('/^data:image\/(jpeg|png|webp|gif);base64,/', $avatarValue)) { + 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); + } + + return $this->ok(['message' => 'Avatar berhasil diperbarui.', 'avatar' => $avatarValue]); + } + public function testEmail(Request $request): JsonResponse { /** @var ApiActor $actor */ diff --git a/server-connection/geonet-console/app/Models/LdapUserMeta.php b/server-connection/geonet-console/app/Models/LdapUserMeta.php index 3f9c76e..7a7f00c 100644 --- a/server-connection/geonet-console/app/Models/LdapUserMeta.php +++ b/server-connection/geonet-console/app/Models/LdapUserMeta.php @@ -8,5 +8,5 @@ class LdapUserMeta extends Model { protected $table = 'ldap_user_meta'; - protected $fillable = ['username', 'other_email', 'phone', 'admin_note']; + protected $fillable = ['username', 'other_email', 'phone', 'admin_note', 'avatar']; } diff --git a/server-connection/geonet-console/app/Services/LdapMetadataService.php b/server-connection/geonet-console/app/Services/LdapMetadataService.php index 3c36bd1..03dc0cc 100644 --- a/server-connection/geonet-console/app/Services/LdapMetadataService.php +++ b/server-connection/geonet-console/app/Services/LdapMetadataService.php @@ -15,6 +15,7 @@ class LdapMetadataService 'other_email' => $row?->other_email ?? '', 'phone' => $row?->phone ?? '', 'admin_note' => $row?->admin_note ?? '', + 'avatar' => $row?->avatar ?? null, ]; } @@ -44,6 +45,14 @@ class LdapMetadataService ); } + public function saveAvatar(string $username, ?string $avatarBase64): void + { + LdapUserMeta::query()->updateOrCreate( + ['username' => $username], + ['avatar' => $avatarBase64], + ); + } + public function getGroupMeta(string $groupKey): array { $row = LdapGroupMeta::query()->where('group_key', $groupKey)->first(); diff --git a/server-connection/geonet-console/database/migrations/2026_06_24_000001_add_avatar_to_ldap_user_meta.php b/server-connection/geonet-console/database/migrations/2026_06_24_000001_add_avatar_to_ldap_user_meta.php new file mode 100644 index 0000000..f61ab84 --- /dev/null +++ b/server-connection/geonet-console/database/migrations/2026_06_24_000001_add_avatar_to_ldap_user_meta.php @@ -0,0 +1,22 @@ +text('avatar')->nullable()->after('phone'); + }); + } + + public function down(): void + { + Schema::table('ldap_user_meta', function (Blueprint $table) { + $table->dropColumn('avatar'); + }); + } +}; diff --git a/server-connection/geonet-console/routes/api.php b/server-connection/geonet-console/routes/api.php index 148d7a0..b0a7864 100644 --- a/server-connection/geonet-console/routes/api.php +++ b/server-connection/geonet-console/routes/api.php @@ -26,6 +26,7 @@ Route::prefix('v1')->group(function () { Route::middleware(['api.auth', 'api.audit'])->group(function () { Route::get('/me', [MeController::class, 'show'])->name('api.v1.me'); Route::put('/me', [MeController::class, 'update'])->name('api.v1.me.update'); + Route::put('/me/avatar', [MeController::class, 'updateAvatar'])->name('api.v1.me.avatar'); Route::post('/me/password', [MeController::class, 'changePassword'])->name('api.v1.me.password'); Route::post('/me/test-email', [MeController::class, 'testEmail'])->name('api.v1.me.test-email');