@@ -110,40 +110,42 @@ public DocumentDto create(String tenantName, Long patientId, DocumentDto dto, Mu
110110 }
111111 }
112112
113- // 6. Upload to S3
113+ // 6. Upload to S3 or fall back to local DB storage
114114 StorageConfig .S3 s3Config = configProvider .getS3DocumentStorage ();
115- if (s3Config == null ) {
116- throw new RuntimeException ("S3 document storage is not configured" );
117- }
118- S3Client s3 = buildS3Client (s3Config );
119-
120- String key = tenantName + "/documents/" + patientId + "/" + UUID .randomUUID () + "_" + file .getOriginalFilename ();
121- try {
122- s3 .putObject (
123- PutObjectRequest .builder ()
124- .bucket (s3Config .getBucket ())
125- .key (key )
126- .contentType (file .getContentType ())
127- .build (),
128- RequestBody .fromBytes (fileBytes )
129- );
130- log .info ("Uploaded to S3 bucket={} key={}" , s3Config .getBucket (), key );
131- } catch (Exception e ) {
132- throw new RuntimeException ("Failed to upload file to S3" , e );
133- }
134-
135- // 7. Save metadata in DB
136115 dto .setPatientId (patientId );
137116 dto .setFileName (file .getOriginalFilename ());
138117 dto .setContentType (file .getContentType ());
139- dto .setS3Bucket (s3Config .getBucket ());
140- dto .setS3Key (key );
141118
142119 Document entity = mapToEntity (dto );
143- entity .setTenantName (tenantName ); // Save tenant name from header
120+ entity .setTenantName (tenantName );
144121 entity .setEncryptionKey (base64Key );
145122 entity .setInitializationVector (base64Iv );
146123
124+ if (s3Config != null ) {
125+ S3Client s3 = buildS3Client (s3Config );
126+ String key = tenantName + "/documents/" + patientId + "/" + UUID .randomUUID () + "_" + file .getOriginalFilename ();
127+ try {
128+ s3 .putObject (
129+ PutObjectRequest .builder ()
130+ .bucket (s3Config .getBucket ())
131+ .key (key )
132+ .contentType (file .getContentType ())
133+ .build (),
134+ RequestBody .fromBytes (fileBytes )
135+ );
136+ log .info ("Uploaded to S3 bucket={} key={}" , s3Config .getBucket (), key );
137+ } catch (Exception e ) {
138+ throw new RuntimeException ("Failed to upload file to S3" , e );
139+ }
140+ entity .setS3Bucket (s3Config .getBucket ());
141+ entity .setS3Key (key );
142+ } else {
143+ // Local DB fallback when S3 is not configured
144+ entity .setContentBase64 (Base64 .getEncoder ().encodeToString (fileBytes ));
145+ log .info ("S3 not configured — stored file content locally for patientId={}" , patientId );
146+ }
147+
148+ // 7. Save metadata (and optionally content) in DB
147149 repository .save (entity );
148150 dto .setId (entity .getId ());
149151 dto .setContent (null );
@@ -156,20 +158,20 @@ public void delete(Long documentId) {
156158 Document document = repository .findById (documentId )
157159 .orElseThrow (() -> new RuntimeException ("Document not found" ));
158160
159- StorageConfig . S3 s3Config = configProvider . getS3DocumentStorage ();
160- if ( s3Config == null ) {
161- throw new RuntimeException ( "S3 document storage is not configured" );
162- }
163- S3Client s3 = buildS3Client (s3Config );
164-
165- try {
166- s3 . deleteObject ( DeleteObjectRequest . builder ( )
167- . bucket ( document . getS3Bucket ())
168- . key ( document .getS3Key ())
169- . build ());
170- log . info ( "Deleted from S3: bucket={}, key={} " , document . getS3Bucket (), document . getS3Key () );
171- } catch ( Exception e ) {
172- throw new RuntimeException ( "Failed to delete file from S3" , e );
161+ if ( document . getS3Key () != null && ! document . getS3Key (). isBlank ()) {
162+ StorageConfig . S3 s3Config = configProvider . getS3DocumentStorage ();
163+ if ( s3Config != null ) {
164+ try {
165+ S3Client s3 = buildS3Client (s3Config );
166+ s3 . deleteObject ( DeleteObjectRequest . builder ()
167+ . bucket ( document . getS3Bucket ())
168+ . key ( document . getS3Key () )
169+ . build ());
170+ log . info ( "Deleted from S3: bucket={}, key={}" , document . getS3Bucket (), document .getS3Key ());
171+ } catch ( Exception e ) {
172+ throw new RuntimeException ( "Failed to delete file from S3 " , e );
173+ }
174+ }
173175 }
174176
175177 repository .delete (document );
@@ -180,42 +182,48 @@ public DownloadResult download(Long documentId) {
180182 Document document = repository .findById (documentId )
181183 .orElseThrow (() -> new RuntimeException ("Document not found" ));
182184
183- StorageConfig .S3 s3Config = configProvider .getS3DocumentStorage ();
184- if (s3Config == null ) {
185- throw new RuntimeException ("S3 document storage is not configured" );
186- }
187- S3Client s3 = buildS3Client (s3Config );
188-
189- try (InputStream is = s3 .getObject (GetObjectRequest .builder ()
190- .bucket (document .getS3Bucket ())
191- .key (document .getS3Key ())
192- .build ())) {
193-
194- byte [] fileBytes = is .readAllBytes ();
195-
196- // 🔑 decrypt if metadata exists
197- if (document .getEncryptionKey () != null && document .getInitializationVector () != null ) {
198- try {
199- byte [] decodedKey = Base64 .getDecoder ().decode (document .getEncryptionKey ());
200- SecretKey key = EncryptionUtil .fromBytes (decodedKey );
201- byte [] iv = Base64 .getDecoder ().decode (document .getInitializationVector ());
185+ byte [] fileBytes ;
202186
203- fileBytes = EncryptionUtil . decrypt ( fileBytes , key , iv );
204- log . info ( "Decrypted file before sending: {}" , document . getFileName ());
205- } catch ( Exception e ) {
206- throw new RuntimeException ( "Failed to decrypt file" , e );
207- }
187+ if ( document . getS3Key () != null && ! document . getS3Key (). isBlank ()) {
188+ // Retrieve from S3
189+ StorageConfig . S3 s3Config = configProvider . getS3DocumentStorage ();
190+ if ( s3Config == null ) {
191+ throw new RuntimeException ( "S3 document storage is not configured" );
208192 }
193+ S3Client s3 = buildS3Client (s3Config );
194+ try (InputStream is = s3 .getObject (GetObjectRequest .builder ()
195+ .bucket (document .getS3Bucket ())
196+ .key (document .getS3Key ())
197+ .build ())) {
198+ fileBytes = is .readAllBytes ();
199+ } catch (Exception e ) {
200+ throw new RuntimeException ("Failed to download file from S3" , e );
201+ }
202+ } else if (document .getContentBase64 () != null && !document .getContentBase64 ().isBlank ()) {
203+ // Retrieve from local DB storage
204+ fileBytes = Base64 .getDecoder ().decode (document .getContentBase64 ());
205+ } else {
206+ throw new RuntimeException ("No file content available for document " + documentId );
207+ }
209208
210- return new DownloadResult (
211- new java .io .ByteArrayInputStream (fileBytes ),
212- document .getContentType (),
213- document .getFileName ()
214- );
215-
216- } catch (Exception e ) {
217- throw new RuntimeException ("Failed to download file from S3" , e );
209+ // Decrypt if metadata exists
210+ if (document .getEncryptionKey () != null && document .getInitializationVector () != null ) {
211+ try {
212+ byte [] decodedKey = Base64 .getDecoder ().decode (document .getEncryptionKey ());
213+ SecretKey key = EncryptionUtil .fromBytes (decodedKey );
214+ byte [] iv = Base64 .getDecoder ().decode (document .getInitializationVector ());
215+ fileBytes = EncryptionUtil .decrypt (fileBytes , key , iv );
216+ log .info ("Decrypted file before sending: {}" , document .getFileName ());
217+ } catch (Exception e ) {
218+ throw new RuntimeException ("Failed to decrypt file" , e );
219+ }
218220 }
221+
222+ return new DownloadResult (
223+ new java .io .ByteArrayInputStream (fileBytes ),
224+ document .getContentType (),
225+ document .getFileName ()
226+ );
219227 }
220228
221229 @ Transactional (readOnly = true )
0 commit comments