Skip to content

GStorage — Implementation Plan

Realizarea design-ului din platform_design.md (sursă de adevăr) + ../gstorage.jdl (model). Strategie: generează scheletul JHipster microservices, apoi adaugă straturile scrise de mână în clase separat denumite (regenerare-safe, ca gnotify/gdocs). Acest document este planul de build autoritativ; ține-l sincron pe măsură ce fazele aterizează.

#DecizieAlegere
1PoziționareStrat de infra sub gdocs: object-store generic + catalog open-data + SDK. Fără semnare/OCR.
2FundațieJHipster microservices 8.11 (Spring Boot 3 + Angular 19)
3Topologiegateway (UI :8080) + gstorage (backend :8100) + JHipster Registry/Eureka (:8761); scalare = N replici gstorage
4Model de dateDouă straturi: fizic (Bucket+ObiectStocat+SesiuneTransfer) + catalog (Organizatie→Dataset→Resursa, Grup)
5Object storeMinIO acum, în spatele driverului StorageBackend (S3-swappable); integritate sha-256 + soft-delete + versionare + backup
6Auth UIDual JWT + Keycloak (realm gstorage), ca glog
7Auth mașinăDual: client-credentials Keycloak (scope storage:* + tenant) SAU cheie API locală (ApiClient) fără Keycloak
8TransferREST multipart + Range reluabil (download) + upload reluabil (SesiuneTransfer) + server FTP/SFTP real (Apache MINA)
9CăutareElasticsearch + worker Tika (extragere text) — hand-wired
10IntegrăriConectori GLog + GNotify (SDK, client-credentials) live și built-in (Alarma engine + Eveniment audit)
11ScalareRabbitMQ workeri + retry/DLQ; gateway+Eureka LB; 2 replici în demo; restart:always+healthcheck = supervizor
12SDKJava + JS construite/publicate în GitLab registry; PHP proiectat
13SecvențiereBackend corect întâi, UI/portal după

1. Faza 0 — Scaffold (gstorage.jdl + generare)

Section titled “1. Faza 0 — Scaffold (gstorage.jdl + generare)”
  • gstorage.jdl = sursă de adevăr (deja scris: 2 aplicații, 13 entități, 12 enum-uri).
  • Generare (Node 20, fără Java local):
    Terminal window
    cd paas_gstorage
    jhipster jdl gstorage.jdl --force # generează gateway/ + gstorage/ + entitățile
    Aceasta creează două subproiecte (gateway/, gstorage/). Alternativ, se pot ține în subfoldere sau se despart în repo-uri; pentru demo rămân în paas_gstorage/.
  • git deja inițializat (remote git.esempla.systems/govtech/gstack/paas_gstorage, branch master).
  • Adaugă CLAUDE.md (deja scris) cu convențiile.
  • Verificare: generarea reușește; ambele apps compilează în container maven:3.9-eclipse-temurin-21; ./npmw run lint verde la gateway.

2. Faza 1 — DB hardening (Liquibase append-only)

Section titled “2. Faza 1 — DB hardening (Liquibase append-only)”
  • Changelog unicitate compusă obiect_stocat (bucket_id, cheie, versiune).
  • Changelog append-only eveniment (trigger BEFORE UPDATE/DELETE) + AppendOnlyAuditGuard (listener Hibernate).
  • Changelog CHECK/validare permisiune (exact un beneficiar + o resursă) — la aplicație; opțional trigger.
  • Normalizează api_client.api_key_hash (BCrypt/Argon2) — hash, niciodată clar.
  • (Hardening amânat) conversie jsonb + index GIN pentru câmpurile JSON (vezi db_schema.md §7).
  • Verificare: ./mvnw -ntp liquibase:updateSQL (în container) generează DDL fără erori.

3. Faza 2 — Object-store + driver StorageBackend

Section titled “3. Faza 2 — Object-store + driver StorageBackend”

Clase noi (regenerare-safe): service/storage/.

  • StorageBackend (interfață, vezi platform_design.md §3) + MinioBackend (client MinIO) + schelet S3Backend (AWS SDK v2).
  • StorageService — orchestrare: la put, calculează sha-256, scrie ObiectStocat (metadate), stochează binarul prin backend, publică eveniment de index (RabbitMQ), scrie Eveniment UPLOAD.
  • Soft-delete (statut=STERS + sters_la), restore, purge (binar + null pe cheie, păstrează rândul+audit), versiuni.
  • src/main/docker/minio.yml + application.yml gstorage.storage.* (backend=minio, endpoint, credențiale, bucket implicit).
  • Verificare: upload → obiect în MinIO + rând obiect_stocat + sha-256; soft-delete → tombstone; restore; purge păstrează audit.

4. Faza 3 — Transfer (REST + reluabil + FTP/SFTP)

Section titled “4. Faza 3 — Transfer (REST + reluabil + FTP/SFTP)”

Clase noi: web/rest/v1/, service/transfer/, ftp/.

  • TransferV1Resource — POST /api/v1/transfer (initiate → SesiuneTransfer), PUT /api/v1/transfer/{uploadId}/part/{n}, GET /api/v1/transfer/{uploadId} (progres, pentru reluare), POST .../complete (verifică sha-256 → ObiectStocat), DELETE .../abort.
  • ObjectV1Resource — PUT /api/v1/object (upload simplu multipart), GET /api/v1/object/{bucket}/{cheie} cu suport HTTP Range (descărcare reluabilă), DELETE (soft), GET .../versions.
  • ftp/ — server Apache MINA (SFTP + FTPS) cu FileSystemView care punte-ază spre StorageBackend; autentificare mapată pe ApiClient/AppUser.
  • Verificare: upload în părți, întrerupere, GET progres, reluare, complete → hash OK; descărcare cu Range reia; conectare SFTP listează/urcă un fișier.

5. Faza 4 — Securitate (dual UI + dual mașină) + ACL

Section titled “5. Faza 4 — Securitate (dual UI + dual mașină) + ACL”

Clase noi: security/apikey/, security/oauth2/, security/acl/, security/local/.

  • gateway: SecurityFilterChain dual — Keycloak OIDC (realm gstorage) + JWT local HS512 (JwtIssuerAuthenticationManagerResolver, ca dualauth din glog); pagina de login cu buton Keycloak + formular user/parolă.
  • gstorage /api/v1: ApiKeyFilter (antet X-Api-Key → ApiClient.apiKeyHash)
    • OAuth2 resource-server (token serviciu Keycloak); ambele → authorities SCOPE_storage:* + tenant.
  • AccessControlService — impune nivelurile (PUBLIC/PRIVAT/RESTRICTIONAT) + granturile Permisiune, la fiecare listare/citire/scriere; niciodată doar în UI.
  • Auto-provisioning: la prima conectare PaaS → creează Organizatie+Bucket+ApiClient.
  • Seed admin/admin (bootstrap) pentru modul standalone.
  • Verificare: cheie API validă → 200; lipsă/invalidă → 401; scope insuficient → 403; tenant greșit → 403; token Keycloak client-credentials → 200; login UI dual.

6. Faza 5 — Async (RabbitMQ) + căutare (Tika + ES) + integrări

Section titled “6. Faza 5 — Async (RabbitMQ) + căutare (Tika + ES) + integrări”

Clase noi: config/RabbitMqConfig, service/search/, service/alarm/, service/arhiva/, connect/, repository/search/.

  • RabbitMQ: exchange + cozi (extract, backup, arhivare, finalizare), retry [10,30,60,120,300]s + DLQ (ca gnotify). spring-boot-starter-amqp.
  • service/search/ExtractWorker — @RabbitListener → Tika extrage text → ObiectStocat.textIndexat + index ES (repository/search/* cu tipuri ES fully-qualified). SearchService + /api/v1/search + fațete portal.
  • service/alarm/AlarmEngine — evaluează Alarma, declanșează pe canal.
  • service/arhiva/ArchiveService — comprimă N obiecte → obiect ZIP (worker).
  • connect/GlogClient (SDK GLog, client-credentials glog-ingest — ca gnotify→glog)
    • connect/GnotifyClient (SDK GNotify). Fiecare Eveniment → și GLog când e configurat; alarmele → GNotify.
  • Joburi @Scheduled: backup, purjare tombstone (30z), expirare sesiuni.
  • Verificare: upload PDF → text căutabil în ES; căutare „cuvinte din fișier”; alarmă STOCARE_PRAG → GNotify; eveniment vizibil în GLog; arhivă ZIP creată.

7. Faza 6 — UI (gateway, Angular + GDS) + portal public

Section titled “7. Faza 6 — UI (gateway, Angular + GDS) + portal public”
  • CRUD generat pentru cele 13 entități (board de administrare), Authority.ADMIN. Eveniment read-only.
  • Ecrane custom (stil gStack, 5-fișiere per componentă):
    • app/explorer/ — explorator de fișiere (listă bucket/obiecte, upload cu progres reluabil, download cu/fără descărcare, preview).
    • app/portal/ — portal public de date deschise: pagini /dataset, /organization, /group (ca dataset.gov.md), căutare + fațete.
    • app/clients/ — gestionare ApiClient (chei API + scope-uri).
    • app/dashboard/ — statistici (spațiu ocupat, obiecte/format, transferuri, top organizații).
  • Auth UI: login dual (Keycloak + user/parolă), stil GDS @gstack/gds-angular.
  • i18n în ambii arbori (RO nativ + EN): webapp/i18n/{ro,en}/*.json + resources/i18n/messages_{ro,en}.properties.
  • Verificare: login dual; CRUD toate entitățile; explorer upload/download; portal public caută + descarcă un dataset public; dashboard actualizat; RO/EN.

8. Faza 7 — SDK (Java + JS reale, PHP proiectat)

Section titled “8. Faza 7 — SDK (Java + JS reale, PHP proiectat)”
  • sdk/java (Maven), sdk/js (npm) — vezi sdk.md. Operații: auth (cheie API sau provider client-credentials Keycloak), upload/download (+ reluabil), bucket/obiect ops, publicare dataset/resursă, URL-uri pre-semnate, căutare.
  • Publicare în GitLab Package Registry (Maven + npm), ca glog.
  • sdk/php (Composer) — schelet + documentat, nepublicat (ca PHP-ul glog).
  • examples/node-demo — flux end-to-end.
  • Verificare: SDK Java + JS urcă/descarcă/caută live; publicate în registry.

9. Faza 8 — Dockerize + deploy (mirror gdocs/gnotify)

Section titled “9. Faza 8 — Dockerize + deploy (mirror gdocs/gnotify)”
  • Dockerfile.gateway (jar) + Dockerfile.gstorage (jar) + docker-compose-server.yml (gateway + 2× gstorage + registry + postgres + minio + elasticsearch + rabbitmq) pe internal + external gstack-web.
  • Împinge registry.esempla.systems/govtech/gstack/paas_gstorage:{gateway,gstorage}-<ver>.
  • Edge nginx vhost gstorage.gstack.esempla.systems + certbot.
  • Keycloak: realm gstorage + clienți gstorage-web (public+PKCE) + gstorage-ingest (confidential, client-credentials) — configurat de utilizator (SSO propriu).
  • Deploy = docker save | ssh | docker load → compose up -d (ca aws-server-and-deploy).
  • Atenție la RAM/disc pe box (ES + MinIO + RabbitMQ + 2 replici — dial la 1 dacă e strâns).

Critical files (noi dacă nu se spune altfel)

Section titled “Critical files (noi dacă nu se spune altfel)”
  • gstorage.jdl (sursă de adevăr) · docs/{platform_design,db_schema,sdk}.md · CLAUDE.md
  • gstorage/.../service/storage/{StorageBackend,MinioBackend,S3Backend,StorageService}.java
  • gstorage/.../web/rest/v1/{ObjectV1Resource,TransferV1Resource,SearchV1Resource,TemplateV1Resource}.java
  • gstorage/.../service/transfer/* · gstorage/.../ftp/* (Apache MINA)
  • gstorage/.../security/{apikey,oauth2,acl,local}/*
  • gstorage/.../config/RabbitMqConfig.java · service/{search,alarm,arhiva}/* · repository/search/*
  • gstorage/.../connect/{GlogClient,GnotifyClient}.java
  • Liquibase: changelog-uri unicitate + append-only + (hardening) jsonb
  • gateway/.../security/* (dual) · Angular app/{explorer,portal,clients,dashboard}/
  • sdk/{java,js,php} · examples/node-demo
  • Dockerfile.{gateway,gstorage} · docker-compose-server.yml · nginx/
  • Citirea systemId/cheii din corpul cererii în filtru — pentru upload-uri streamate, autentifică din antet (X-Api-Key/Authorization), nu din body.
  • RabbitMQ + ES + MinIO + 2 replici — greu pe box-ul cu RAM limitat; dial la 1 replică dacă e nevoie (design-ul suportă N).
  • FTP/SFTP (MINA) — suprafață în plus (port, auth bridging); rulează într-un singur worker/instanță ca să nu dublezi sesiunile.
  • JDL = sursă de adevăr — toată logica scrisă de mână în clase separat denumite (*V1Resource, StorageBackend, *Worker, ftp/*) ca regenerarea să nu o strice.
  • Eveniment append-only — forensic; conectorul GLog nu înlocuiește jurnalul intern.
  • Coliziune de nume ES — evitată prin hand-wiring (nu declarăm search în JDL).